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信號,盡量簡化代碼,不要有冗余代碼