RTL视图:
此次修改了串口模块,和FIFO控制模块。
串口模块:以后遇到FIFO位宽不管是8位 或 16位 或 32位 ,串口模块都可以通用,而不需要根据FIFO的位宽再做相应更改。输入的data_in位宽定义的是8位, 上个“实验1”中data_in是定义16位宽,要连续发送两个8位数据,可参考上一篇文章。
FIFO模块:增加了一个计数器,控制取数据,fifo 读出一个数据是16bit的,而串口一次只能发8位,所以,在读出数据之前,就得把fifo上的数据取走,只有取走发出去之后,才能读fifo中的一下个数据。FIFO增加了异步清除,在做仿真抓时序时,
按住复位按键,将FIFO数据清空,这样可以触发抓到第一个数据波形,要不然老是触发到快满时的波形,和串口打印的数据就不好对比了。(串口发送效率变慢了,很容易触发usedw>250条件)
串口模块代码:
1 module uart_txd( 2 clk, 3 rst_n, 4 txd_din_vld, 5 data_din, 6 txd_rdy, 7 txd_dout 8 ); 9 10 parameter DATA_W = 8; 11 parameter BAUD_RATE = 54; 12 13 input clk; 14 input rst_n; 15 input txd_din_vld; 16 input [DATA_W-1:0]data_din; 17 18 output txd_rdy; 19 output txd_dout; 20 21 wire add_cnt0/* synthesis keep*/; 22 wire end_cnt0/* synthesis keep*/; 23 24 wire add_cnt1; 25 wire end_cnt1; 26 27 wire [10-1:0]data_temp; 28 29 reg cnt0_vld; 30 always @(posedge clk or negedge rst_n)begin 31 if(!rst_n)begin 32 cnt0_vld <= 0; 33 end 34 else if(txd_din_vld)begin 35 cnt0_vld <= 1; 36 end 37 else if(end_cnt1)begin 38 cnt0_vld <= 0; 39 end 40 end 41 42 reg [8:0] cnt0; 43 always @(posedge clk or negedge rst_n)begin 44 if(!rst_n)begin 45 cnt0 <= 0; 46 end 47 else if(add_cnt0)begin 48 if(end_cnt0)begin 49 cnt0 <= 0; 50 end 51 else begin 52 cnt0 <= cnt0 + 1; 53 end 54 end 55 end 56 57 assign add_cnt0 = cnt0_vld == 1; 58 assign end_cnt0 = add_cnt0 && cnt0 == BAUD_RATE - 1; 59 60 reg [3:0] cnt1; 61 always @(posedge clk or negedge rst_n)begin 62 if(!rst_n)begin 63 cnt1 <= 0; 64 end 65 else if(add_cnt1)begin 66 if(end_cnt1)begin 67 cnt1 <= 0; 68 end 69 else begin 70 cnt1 <= cnt1 + 1; 71 end 72 end 73 end 74 75 assign add_cnt1 = end_cnt0; 76 assign end_cnt1 = add_cnt1 && cnt1 == (DATA_W + 1 + 1) - 1; //数据位宽+起始位+停止位 77 78 reg[DATA_W-1 : 0] data_buf; 79 always @(posedge clk or negedge rst_n)begin 80 if(!rst_n)begin 81 data_buf <= 0; 82 end 83 else if(txd_din_vld)begin //在检测FIFO输出的有效信号时,把数据进行锁存,避免在发送过程中,data_buf 数据发生变化 84 data_buf <= data_din; 85 end 86 end 87 88 assign data_temp = {1'b1, data_buf, 1'b0}; // 停止位 + 8bit数据 + 起始位, 低位先发 89 90 reg txd_dout; 91 always @(posedge clk or negedge rst_n)begin 92 if(!rst_n)begin 93 txd_dout <= 1; 94 end 95 else if(add_cnt0 && cnt0 == 0 && cnt1 >=0 && cnt1 < (DATA_W + 1 + 1))begin 96 txd_dout <= data_temp[cnt1]; 97 end 98 end 99 100 assign txd_rdy = (cnt0_vld || txd_din_vld )? 1'b0: 1'b1; 101 102 endmodule
fifo模块代码:
1 module control_fifo( 2 clk, 3 rst_n, 4 din_vld, 5 fifo_data_din, 6 din_rdy,//下游模块准备好信号 7 fifo_dout_vld, //通知下游模块准备收数据 8 fifo_data_dout, 9 fifo_full_flag 10 ); 11 parameter DATA_WRW = 16; 12 input clk; 13 input rst_n; 14 input din_vld; 15 input [DATA_WRW-1:0] fifo_data_din; 16 input din_rdy; 17 18 output fifo_dout_vld; 19 output[8-1:0] fifo_data_dout; 20 output fifo_full_flag; 21 22 reg fifo_full_flag; 23 wire rdreq; 24 reg wrreq; 25 wire [DATA_WRW-1:0] q/* synthesis keep*/; 26 wire [7:0]usedw/* synthesis keep*/; 27 wire add_cnt0; 28 wire end_cnt0; 29 my_fifo my_fifo_inst ( 30 .aclr ( ~rst_n ), 31 .clock ( clk ), 32 .data ( fifo_data_din ), 33 .rdreq ( rdreq ), 34 .wrreq ( wrreq ), 35 .empty ( empty ), 36 .full ( full), 37 .q ( q ), 38 .usedw ( usedw) 39 ); 40 41 //assign wrreq = full? 1'b0 : din_vld; 42 43 always @(*)begin 44 if(usedw >= 250)begin 45 wrreq = 0; 46 fifo_full_flag = 0; 47 end 48 else begin 49 wrreq = din_vld; 50 fifo_full_flag = 1; 51 end 52 53 end 54 55 reg [0:0] cnt0; 56 always @(posedge clk or negedge rst_n)begin 57 if(!rst_n)begin 58 cnt0 <= 0; 59 end 60 else if(add_cnt0)begin 61 if(end_cnt0)begin 62 cnt0 <= 0; 63 end 64 else begin 65 cnt0 <= cnt0 + 1; 66 end 67 end 68 end 69 70 assign add_cnt0 = (empty == 0) && (din_rdy == 1); 71 assign end_cnt0 = add_cnt0 && cnt0 == 2-1; 72 73 assign rdreq = end_cnt0; 74 75 reg [8-1:0] fifo_data_dout; 76 always @(posedge clk or negedge rst_n)begin 77 if(!rst_n)begin 78 fifo_data_dout <= 0; 79 end 80 else if(add_cnt0 && cnt0 >=0 && cnt0 < 2)begin 81 fifo_data_dout <= q[16-1-8*cnt0 -:8]; 82 end 83 end 84 85 reg fifo_dout_vld; 86 always @(posedge clk or negedge rst_n)begin 87 if(!rst_n)begin 88 fifo_dout_vld <= 0; 89 end 90 else begin 91 fifo_dout_vld <= add_cnt0; 92 end 93 end 94 95 endmodule
仿真时序和串口数据对比,结果数据也是对的,没有丢数据
时序说明:在读使能有效之前,就得将FIFO 总线上q的数据分两次进行发送,一次发送8bit (先高8bit,在发低8bit)。FIFO是用的是show-ahead模式,在读使能有效的同时,数据已经在输出总线上了,也就是FIFO的q。
注意几个信号:
(1)、din_rdy 有效高电平只能保持一个时钟周期,否则读出的FIFO数据将会丢失。如果din_rdy 有效信号保持两个时钟周期就得查该问题出在哪。
(2)、rdreq 读使能有效电平也只能保持一个时钟周期,一旦连续保持两个时钟周期,FIFO的数据肯定会丢,记住串口发送很慢很慢,你得等等,让我发完才行
(3)、强调,写使能 和 要写的数据 必须保持在同一拍,如果不在同一拍上,可采用D触发器进行延时达到保持在同一拍。
(4)、强调, 读使能有效期间,q有效数据也是在同一拍上,所以不能通过时序逻辑判断rdreq 信号读数据,得用组合逻辑。在有效之前,就可以先发高8bit数据。也只能保持一个时钟周期。
记住这些要点,可以帮助你快速定位问题。
错误写法:
alwaya @(posedge clk or negedge rst_n )begin
if()
else if(rdreq==1)
data <= q;
end
正确写法用组合逻辑:assign rdreq = *** ;
ad7928模块:(代码未改动,和实验1 代码一样)
1 module ad7928( 2 clk, 3 rst_n, 4 adc_dout, 5 adc_cs, 6 adc_sclk, 7 adc_din, 8 din_vld, 9 adc_dout_vld, 10 adc_data_out 11 ); 12 13 parameter WRITE = 1'b1 ; 14 parameter SEQ = 1'b0 ; 15 parameter PM1 = 1'b1 ; 16 parameter PM0 = 1'b1 ; 17 parameter SHADOW = 1'b0 ; 18 parameter RANGE = 1'b0 ; 19 parameter CODING = 1'b1 ; 20 parameter ADDRES = 3'b010; 21 22 input clk ; 23 input rst_n ; 24 input adc_dout; 25 input din_vld ; 26 27 output adc_cs ; 28 output adc_sclk; 29 output adc_din ; 30 output adc_dout_vld; 31 output [16-1:0] adc_data_out; 32 33 wire add_cnt0; 34 wire end_cnt0; 35 36 wire add_cnt1; 37 wire end_cnt1; 38 39 wire add_cnt2; 40 wire end_cnt2; 41 42 wire [15:0] data; 43 44 reg [2:0] cnt0; 45 always @(posedge clk or negedge rst_n)begin 46 if(!rst_n)begin 47 cnt0 <= 0; 48 end 49 else if(add_cnt0)begin 50 if(end_cnt0)begin 51 cnt0 <= 0; 52 end 53 else begin 54 cnt0 <= cnt0 + 1; 55 end 56 end 57 end 58 59 assign add_cnt0 = din_vld; 60 assign end_cnt0 = add_cnt0 && cnt0 == 4-1; 61 62 63 reg [4:0] cnt1; 64 always @(posedge clk or negedge rst_n)begin 65 if(!rst_n)begin 66 cnt1 <= 0; 67 end 68 else if(add_cnt1)begin 69 if(end_cnt1)begin 70 cnt1 <= 0; 71 end 72 else begin 73 cnt1 <= cnt1 + 1; 74 end 75 end 76 end 77 78 assign add_cnt1 = end_cnt0; 79 assign end_cnt1 = add_cnt1 && cnt1 == 17-1; 80 81 reg [3:0] cnt2; 82 always @(posedge clk or negedge rst_n)begin 83 if(!rst_n)begin 84 cnt2 <= 0; 85 end 86 else if(add_cnt2)begin 87 if(end_cnt2)begin 88 cnt2 <= 0; 89 end 90 else begin 91 cnt2 <= cnt2 + 1; 92 end 93 end 94 end 95 96 assign add_cnt2 = end_cnt1; 97 assign end_cnt2 = add_cnt2 && cnt2 == 8-1; 98 99 100 reg adc_sclk; 101 always @(posedge clk or negedge rst_n)begin 102 if(!rst_n)begin 103 adc_sclk <= 1; 104 end 105 else if(add_cnt0 && cnt0 >= 2 && cnt0 < 4)begin 106 adc_sclk <= 0; 107 end 108 else if(add_cnt0 && cnt0 >= 0 && cnt0 < 2)begin 109 adc_sclk <= 1; 110 end 111 end 112 113 reg adc_cs; 114 always @(posedge clk or negedge rst_n)begin 115 if(!rst_n)begin 116 adc_cs <= 1; 117 end 118 else if(add_cnt0 && cnt0 == 2-1 && cnt1 == 1-1)begin 119 adc_cs <= 0; 120 end 121 else if(add_cnt0 && cnt0 == 2-1 && cnt1 == 17-1)begin 122 adc_cs <= 1; 123 end 124 end 125 126 reg [2:0] channel_sel; 127 always @(posedge clk or negedge rst_n)begin 128 if(!rst_n)begin 129 channel_sel <= 3'b000; 130 end 131 else begin 132 case(cnt2) 133 0 : channel_sel <= 3'b000; 134 1 : channel_sel <= 3'b001; 135 2 : channel_sel <= 3'b010; 136 3 : channel_sel <= 3'b011; 137 4 : channel_sel <= 3'b100; 138 5 : channel_sel <= 3'b101; 139 6 : channel_sel <= 3'b110; 140 7 : channel_sel <= 3'b111; 141 default : channel_sel <= 3'b000; 142 endcase 143 end 144 end 145 146 reg adc_din; //给ADC送数据,进行通道切换 147 always @(posedge clk or negedge rst_n)begin 148 if(!rst_n)begin 149 adc_din <= 1; 150 end 151 else if(add_cnt0 && cnt0 == 2-1 && cnt1 >=0 && cnt1 < 16)begin 152 adc_din = data[15-cnt1]; 153 end 154 end 155 156 assign data = {WRITE, SEQ, 1'b0, channel_sel, PM1, PM0, SHADOW, 1'b0, RANGE, CODING, 4'b0000}; 157 158 reg [16-1:0] data_temp; //从ADC上读数据 159 always @(posedge clk or negedge rst_n)begin 160 if(!rst_n)begin 161 data_temp <= 15'b000_0000_0000_0000; 162 end 163 else if(add_cnt0 && cnt0 == 3-1 && cnt1 >= 0 && cnt1 < 16)begin 164 data_temp[15-cnt1] <= adc_dout; 165 end 166 end 167 168 reg [16-1:0] adc_data_out; //将收到的完整数据进行锁存 169 always @(posedge clk or negedge rst_n)begin 170 if(!rst_n)begin 171 adc_data_out <= 0; 172 end 173 else if(end_cnt1)begin 174 adc_data_out <= data_temp; 175 end 176 end 177 178 reg adc_dout_vld; //数据有效时,同时产生一个有效标志 179 always @(posedge clk or negedge rst_n)begin 180 if(!rst_n)begin 181 adc_dout_vld <= 0; 182 end 183 else if(end_cnt1)begin 184 adc_dout_vld <= 1; 185 end 186 else begin 187 adc_dout_vld <= 0; 188 end 189 end 190 191 endmodule
ad7928_fifo_top模块:(做了位宽修改)
1 module ad7928_fifo_top( 2 clk, 3 rst_n, 4 adc_dout, 5 6 adc_cs, 7 adc_sclk, 8 adc_din, 9 txd_dout 10 ); 11 12 input clk; 13 input rst_n; 14 input adc_dout; 15 16 output adc_cs; 17 output adc_sclk; 18 output adc_din; 19 output txd_dout; 20 21 wire [8-1 : 0] fifo_data_dout; 22 wire [16-1 : 0] adc_data_out; 23 wire adc_dout_vld; 24 wire txd_rdy; 25 wire fifo_dout_vld; 26 wire fifo_full_flag; 27 28 ad7928 u1_adc( 29 .clk (clk), 30 .rst_n (rst_n), 31 .adc_dout (adc_dout), 32 .adc_cs (adc_cs), 33 .adc_sclk (adc_sclk), 34 .adc_din (adc_din), 35 .din_vld (fifo_full_flag), 36 .adc_dout_vld (adc_dout_vld), 37 .adc_data_out (adc_data_out) 38 ); 39 40 control_fifo u2_fifo( 41 .clk(clk), 42 .rst_n(rst_n), 43 .din_vld(adc_dout_vld), 44 .fifo_data_din(adc_data_out), 45 .din_rdy(txd_rdy),//下游模块准备好信号 46 .fifo_dout_vld(fifo_dout_vld), //通知下游模块准备收数据 47 .fifo_data_dout(fifo_data_dout), 48 .fifo_full_flag(fifo_full_flag) 49 ); 50 51 uart_txd u3_uart_txd( 52 .clk(clk), 53 .rst_n(rst_n), 54 .txd_din_vld(fifo_dout_vld), 55 .data_din(fifo_data_dout), 56 .txd_rdy(txd_rdy), 57 .txd_dout(txd_dout) 58 ); 59 60 endmodule
实验1 和 实验2 差别:
实验1:
优点 :写代码简单,好控制,不需要将FIFO 16位宽的数据进行拆分发,效率会更高。
缺点: 不同位宽的FIFO,串口发送模块要做相应的更改,通用性不强。
实验2:
优点:通用性好,不同位宽的FIFO,只需对FIFO控制模块进行更改,串口发送模块不需要做任何更改。
缺点:需要额外增加计数器,控制FIFO数据,将数据拆分发送,效率慢。
代码中可以改进的地方:
adc_sclk信号赋值,代码中用的范围赋值,可以更改为在某个点进行赋值
在某个点对adc_sclk进行赋值:
重复的条件可以另起一个变量名代替,如adc_cs信号,尽量简化代码,不要有冗余代码