一、實驗內容
? ? ? 用一個真雙端RAM,端口A和端口B同時向RAM里寫入數據0-99,A端口讀出單數并存入單端口RAM1中,B端口讀出雙數并存入但端口RAM2中,當檢測到按鍵1到來時將RAM1中的單數讀出顯示到PC端,當檢測到按鍵2到來時,將RAM2中的雙數顯示到pc端。
二、信號流向圖
TIPS:這里我本來想將single_ram_1和single_ram_2分成兩個單獨的模塊,但是經過實驗后發現,如果分成兩個單獨的模塊的話會導致:
? ? ? ?①兩個單端RAM模塊的tx_start(0或1)都會有值給uart_tx模塊,即使是RAM1給【1】,RAM2中沒有值【0】,uart_tx模塊是無法判斷 tx_start 到底來自于哪個模塊,所以此時uart_tx模塊只是能接收到一個tx_start的脈沖信號,但是無法判斷信號來自哪個RAM模塊,無法獲取到相應的uart_data ,最終導致tx_flag都無法變成高電平,那就更不會返回給RAM模塊tx_done信號了。
? ? ? ?②如果單獨為了正確信號能賦值給tx_start而重新去寫一個ctrl模塊的話,那么在ctrl模塊中將無法使用判斷條件,因為我們將狀態作為了賦值條件而不僅僅是key_flag信號。
? ? ? ?那么我的解決方法就是把single_ram_2例化到single_ram_1當中,將single_ram_2輸出的數據(uart_data_b及tx_start_b)和single_ram_1輸出的數據(douta即tx_start_a)全部放在一個模塊即single_ram_1中去做判斷,但是我們仍然無法將single_ram_2的狀態作為賦值的條件,所以只能采用這種比較粗暴的方式,也就是除了(?cur_state == REG || cur_state == READ?)時候tx_start <= tx_start_a ;那么其他情況就是tx_start <= tx_start_b ; uart_data的處理也是同樣。詳見后面程序。
? ? ? ?其實最好的方式是將single_ram_2和single_ram_1寫在同一個模塊中,程序放在文章最后了。
三、程序設計
(1)按鍵消抖模塊:
這里注意key1和key2不能使用同一個計數器,不然在同一個模塊中也會判斷出問題。
`timescale 1ns / 1ps
module key_debounce(input sys_clk ,input rst_n ,input key1 ,input key2 ,output key_flag_1 ,output key_flag_2);
// parameter delay = 100_000_0 ; //20msparameter delay = 100;// 測試用reg[19:0] cnt1 ;reg[19:0] cnt2 ;key_flag_1 always@(posedge sys_clk )if(!rst_n)cnt1 <= 0 ;else if ( key1 == 0 )beginif ( cnt1 == delay -1 )cnt1 <= cnt1 ;else cnt1 <= cnt1 +1 ;endelsecnt1 <= 0 ;assign key_flag_1 = ( cnt1 == delay -2 )?1:0 ;///key_flag_2always@(posedge sys_clk )if(!rst_n)cnt2 <= 0 ;else if ( key2 == 0 )beginif ( cnt2 == delay -1 )cnt2 <= cnt2 ;else cnt2 <= cnt2 +1 ;endelsecnt2 <= 0 ;assign key_flag_2 = ( cnt2 == delay -2 )?1:0 ; endmodule
(2)真雙端模塊:
IP參數:
`timescale 1ns / 1ps
module the_true_ram(input sys_clk ,input rst_n ,output [7:0] ram_odd_data ,output [7:0] ram_even_data);A端口reg wea ;reg [6 : 0] addra ;reg [7 : 0] dina ;wire [7 : 0] douta ;always@(posedge sys_clk )if(!rst_n)wea <= 0 ;else if ( addra >= 99 )wea <= 0 ;elsewea <= 1 ;always@(posedge sys_clk )if(!rst_n)addra <= 0 ;else if ( addra >= 99 )addra <= 99 ;elseaddra <= addra +1 ;always@(posedge sys_clk )if(!rst_n)dina <= 0 ;else if (dina >= 99)dina <= 99 ;elsedina <= dina +1 ;wire [7:0] data_a ;assign data_a = douta ;assign ram_odd_data = (data_a%2 == 1)?data_a : ram_odd_data ;///b端口reg web ; reg [6 : 0] addrb ;reg [7 : 0] dinb ;wire [7 : 0] doutb ;always@(posedge sys_clk )if(!rst_n)web <= 0 ;else if ( addrb >= 99 )web <= 0 ;elseweb <= 1 ;always@(posedge sys_clk )if(!rst_n)addrb <= 0 ;else if ( addrb >= 99 )addrb <= 99 ;elseaddrb <= addrb +1 ;always@(posedge sys_clk )if(!rst_n)dinb <= 0 ;else if ( dinb >= 99 )dinb <= 99 ;elsedinb <= dinb +1 ;wire[7:0] data_b ;assign data_b = doutb ;assign ram_even_data = (data_b %2 == 0 )? data_b : ram_even_data ;//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
true_ram your_instance_name (.clka(sys_clk ), // input wire clka.ena(1), // input wire ena.wea(wea), // input wire [0 : 0] wea.addra(addra), // input wire [6 : 0] addra.dina(dina), // input wire [7 : 0] dina.douta(douta), // output wire [7 : 0] douta.clkb(sys_clk ), // input wire clkb.enb(1), // input wire enb.web(web), // input wire [0 : 0] web.addrb(addrb), // input wire [6 : 0] addrb.dinb(dinb), // input wire [7 : 0] dinb.doutb(doutb) // output wire [7 : 0] doutb
);
// INST_TAG_END ------ End INSTANTIATION Template ---------endmodule
(3)單端RAM2模塊:
但其實深度在50就夠用了。(100里面的奇數和偶數50)
`timescale 1ns / 1ps
module single_ram_2_FMS(input sys_clk ,input rst_n ,input key_flag_2 ,input tx_done ,input [7:0] ram_even_data ,output reg tx_start_b ,output reg[7:0] uart_data_b);存雙數的RAMreg ena ;reg [0 : 0] wea ;reg [6 : 0] addra ;reg [7 : 0] dina ;wire [7 : 0] douta ;//先寫再讀出localparam IDLE = 3'd0 ;localparam WRITE = 3'd1 ;localparam REG = 3'd2 ;localparam READ = 3'd3 ;reg[2:0] cur_state ;reg[2:0] next_state ;//state1always@(posedge sys_clk )if(!rst_n)cur_state <= IDLE ;else cur_state <= next_state ;//state2always@(*)case(cur_state)IDLE :beginnext_state = WRITE ;endWRITE :beginif ( key_flag_2 )next_state = REG ;elsenext_state = cur_state ;endREG :beginnext_state = READ ;endREAD :beginif(addra == 49)next_state = IDLE ;elsenext_state <= cur_state ;enddefault:;endcase//state3always@(posedge sys_clk )if(!rst_n)beginena <= 0 ;wea <= 0 ;addra <= 0 ;dina <= 0 ;tx_start_b <= 0 ;endelsecase(cur_state)IDLE :beginena <= 0 ;wea <= 0 ;addra <= 0 ;dina <= ram_even_data ;endWRITE :beginena <= 1 ;wea <= 1 ;if(addra == 49)addra <= 49 ;elseaddra <= addra +1 ;dina <= ram_even_data ;endREG :beginaddra <= 0 ;ena <= 0 ;wea <= 0 ;dina <= 0 ;tx_start_b <= 1 ;endREAD :beginena <= 1 ;wea <= 0 ;dina<= 0 ;if(tx_done)begintx_start_b <= 1 ;addra <= addra +1 ;endelse begintx_start_b <= 0 ;addra <= addra ;endenddefault:;endcase//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
single_ram ram2 (.clka(sys_clk ), // input wire clka.ena(ena), // input wire ena.wea(wea), // input wire [0 : 0] wea.addra(addra), // input wire [6 : 0] addra.dina(dina), // input wire [7 : 0] dina .douta(douta) // output wire [7 : 0] douta
);
// INST_TAG_END ------ End INSTANTIATION Template ---------always@(posedge sys_clk )if(!rst_n)uart_data_b <= 0 ;else if ( cur_state == READ )uart_data_b <= douta ;elseuart_data_b <= uart_data_b ;always@(posedge sys_clk )if(!rst_n)tx_start_b <= 0 ;else if ( cur_state == REG || cur_state == READ )tx_start_b <= 1 ;elsetx_start_b <= tx_start_b ; endmodule
(4)單端RAM1模塊
配置和前面一樣
`timescale 1ns / 1ps
module single_ram_1_FMS(input sys_clk ,input rst_n ,input key_flag_1 ,input key_flag_2 ,input tx_done ,input[7:0] ram_odd_data ,input[7:0] ram_even_data,output reg tx_start ,output reg[7:0] uart_data );/讀單數的RAMreg ena ; reg wea ;reg [6 : 0] addra ;reg [7 : 0] dina ;wire [7 : 0] douta ;reg tx_start_a ;wire tx_start_b ; wire[7:0] uart_data_b ; 先寫再讀出localparam IDLE = 3'd0 ;localparam ERITE = 3'd1 ;localparam REG = 3'd2 ;localparam READ = 3'd3 ;reg[2:0] cur_state ;reg[2:0] next_state ;//state1always@(posedge sys_clk )if(!rst_n)cur_state <= IDLE ;elsecur_state <= next_state ;//state2always@(*)case(cur_state)IDLE :beginnext_state = ERITE ;endERITE :beginif(key_flag_1)next_state = REG ;elsenext_state <= cur_state ;endREG :beginnext_state = READ ;//用來發送tx_startendREAD :beginif(addra == 49)//100內的單數是50next_state = IDLE ;elsenext_state = cur_state ;enddefault:;endcase//state3always@(posedge sys_clk )if(!rst_n)beginena <= 0 ;wea <= 0 ;addra<= 127 ;dina <= 0 ;tx_start_a <= 0 ;endelsecase(cur_state)IDLE :beginena <= 0 ;wea <= 0 ;addra<= 7'd127 ;dina <= ram_odd_data ;endERITE :beginena <= ~ena ;wea <= ~wea ;if( addra == 49 && wea)addra <= 49 ;else if(wea)addra <= addra +1 ;dina <= ram_odd_data ;endREG :beginena <= 0 ; wea <= 0 ; addra<= 0 ; dina <= 0 ; tx_start_a <= 1 ; endREAD :beginena <= 1 ;wea <= 0 ;dina<= 0 ;if(tx_done)begintx_start_a <= 1 ;addra <= addra +1 ;endelse begintx_start_a <= 0 ;addra <= addra ;endenddefault:;endcase//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG
single_ram ram1 (.clka(sys_clk ), // input wire clka.ena(ena), // input wire ena.wea(wea), // input wire [0 : 0] wea.addra(addra), // input wire [6 : 0] addra.dina(dina), // input wire [7 : 0] dina.douta(douta) // output wire [7 : 0] douta
);
// INST_TAG_END ------ End INSTANTIATION Template ---------always@(posedge sys_clk )if(!rst_n)uart_data <= 0;else if ( cur_state == READ )uart_data <= douta ;elseuart_data <= uart_data_b ;always@(posedge sys_clk )if(!rst_n)tx_start <= 0 ;else if ( cur_state == REG || cur_state == READ )tx_start <= tx_start_a ;elsetx_start <= tx_start_b ; 例化ram2 single_ram_2_FMS single_ram_2_FMS_u(. sys_clk (sys_clk ) ,. rst_n (rst_n ) ,. key_flag_2 (key_flag_2 ) ,. tx_done (tx_done ) ,. ram_even_data (ram_even_data) ,. tx_start_b (tx_start_b ) ,. uart_data_b (uart_data_b )); endmodule
(5)uart_tx模塊:
`timescale 1ns / 1ps
module uart_tx(input sys_clk ,input rst_n ,input wire[7:0] uart_data ,input rx_done , output reg tx_data , output reg tx_done);parameter SYSCLK = 50_000_000 ;parameter Baud = 115200 ;parameter COUNT = SYSCLK/Baud ;//434 傳輸1比特所需要的時鐘周期parameter MID = COUNT/2 ;wire start_flag ;reg tx_flag ;reg tx_reg1 ;reg tx_reg2 ;reg[4:0] cnt_bit ;reg[10:0] cnt ;//tx_startalways@(posedge sys_clk)if(!rst_n)begintx_reg1 <= 0 ;tx_reg2 <= 0 ;endelse begintx_reg1 <= rx_done ;tx_reg2 <= tx_reg1 ;endassign start_flag = tx_reg1 & ~tx_reg2 ;///tx_flagalways@(posedge sys_clk)if(!rst_n)tx_flag <= 0 ;else if ( start_flag == 1 )tx_flag <= 1 ;else if ( cnt == COUNT -1 && cnt_bit == 10)
// else if ( cnt == MID -1 && cnt_bit == 10)tx_flag <= 0 ;elsetx_flag <= tx_flag ;///計時器// cnt 434 always@(posedge sys_clk )if(!rst_n)cnt <= 0;else if ( tx_flag == 1 )beginif ( cnt == COUNT -1) ///一定要減一,如果不減一,實際會計到435次,反算回去波特率就不是115200了cnt <= 0;elsecnt <= cnt +1 ;endelsecnt <= 0 ;// /計數器always@(posedge sys_clk )if(!rst_n)cnt_bit <= 0 ;else if ( tx_flag )beginif ( cnt == COUNT -1)beginif(cnt_bit == 10)///0123456789 10cnt_bit <= 0 ;elsecnt_bit <= cnt_bit +1 ;endelsecnt_bit <= cnt_bit ;endelsecnt_bit <= 0 ;parameter MODE_CHECK = 0 ;always@(posedge sys_clk )if(!rst_n)tx_data <= 1 ; //表示沒有數據else if ( tx_flag )beginif ( cnt_bit > 0 && cnt_bit < 9 )///cnt_bit 0 12345678 9 ///tx_data 0123456789///uart_data 01234567tx_data <= uart_data [cnt_bit-1]; //這里uart_data是不斷隨著cnt_bit變化的,只有在第九位的時候才有正確的最終值else if(cnt_bit == 0)tx_data <= 0 ;else if(cnt_bit == 9)tx_data <= (MODE_CHECK == 0)? ^uart_data: ~^uart_data;/*MODE_CHECK == 0是偶校驗,假如uart_data是1110_0000,其異或的結果是1,將異或的結果作為校驗位,讓數據位和校驗位異或的結果為0,滿足偶校驗。假如uart_data是1110_1000,其異或的結果是0,將異或的結果作為校驗位,讓數據位和校驗位異或的結果為0,滿足偶校驗。奇校驗則相反。*/else if (cnt_bit == 10)///停止位tx_data <= 1 ;elsetx_data <= tx_data ;endelsetx_data <= 1 ;always@(posedge sys_clk )if(!rst_n) tx_done <= 0 ;else if (tx_flag)beginif ( cnt_bit == 10 && cnt == COUNT -1)
// if ( cnt_bit == 10 && cnt == MID/2 -1)tx_done <= 1 ;elsetx_done <= 0 ; endelsetx_done <= 0 ;
endmodule
四、仿真模塊
(1)仿真true_ram模塊
代碼:
`timescale 1ns / 1ps
module test_the_true_ram( );reg sys_clk ;reg rst_n ;wire [7:0] ram_odd_data ;wire [7:0] ram_even_data ;initialbeginsys_clk = 0 ;rst_n = 0 ;#10 rst_n = 1 ;endalways #1 sys_clk = ~sys_clk ; the_true_ram the_true_ram_1(. sys_clk (sys_clk ) ,. rst_n (rst_n ) ,. ram_odd_data (ram_odd_data ) ,. ram_even_data (ram_even_data));endmodule
仿真結果:
(2)仿真TOP:
代碼:
`timescale 1ns / 1ps
module test_TOP( );reg sys_clk ;reg rst_n ;reg key_1 ;reg key_2 ;wire tx_data ;initialbeginsys_clk = 0 ;rst_n = 0 ;key_1 = 1 ;key_2 = 1 ;#10rst_n = 1 ;#10000key_1 = 0 ;endalways #1 sys_clk = ~sys_clk ;TOP TOP_1(. sys_clk (sys_clk) ,. rst_n (rst_n ) ,. key_1 (key_1 ) ,. key_2 (key_2 ) ,. tx_data (tx_data) );endmodule
?仿真結果:
TOP:
single_ram_1 :
五、需要注意的一些問題
(1)
(2)
(3)控制模塊最好這么寫
`timescale 1ns / 1ps
module single_ram_2(input sysclk ,input rst_n ,input key_flag1 ,input key_flag2 ,input tx_done ,input [7:0] ram_odd_data , //單數input [7:0] ram_even_data , //雙數output reg tx_start ,output reg [7:0] uart_data );
//存單數的RAM
reg wea ;
reg ena ;
reg [6:0] addra ;
reg [7:0] dina ;
wire [7:0] douta ;
///先寫再讀出
localparam IDLE = 3'd0;
localparam WRITE = 3'd1; 地址加1
localparam REG = 3'd2; ///緩沖狀態 地址清零
localparam READ = 3'd3;
reg [2:0] cur_state,next_state;
reg tx_start_a ;
always@(posedge sysclk)if(!rst_n)cur_state <= IDLE;elsecur_state <= next_state;
always@(*)case(cur_state)IDLE : beginif(key_flag1)next_state = WRITE;elsenext_state = cur_state;end WRITE :beginif(addra >= 49)next_state = REG;elsenext_state = cur_state; endREG :beginnext_state = READ;endREAD :beginif(addra >= 49)next_state = IDLE;elsenext_state = cur_state; enddefault:;endcase
always@(posedge sysclk)if(!rst_n)beginaddra <= 0;wea <= 0;ena <= 0;dina <= 0;tx_start_a <= 0;endelsecase(cur_state)IDLE :beginaddra <= 0;wea <= 0;ena <= 0;dina <= ram_odd_data; 維持2個endWRITE :begin ///99/48ena <= ~ena; ///wea <= ~wea; ///if(addra >= 49)addra <= 49;else if(wea)addra <= addra + 1; dina <= ram_odd_data;endREG :beginaddra <= 0;ena <= 0;wea <= 0;dina <= 0;tx_start_a <= 1; ///發送第一個數據endREAD :begin ena <= 1;wea <= 0;dina <= 0;if(tx_done)begintx_start_a <= 1;addra <= addra + 1;endelse begintx_start_a <= 0;addra <= addra;end enddefault:; endcaseblk_mem_gen_2 ram_a (.clka(sysclk), // input wire clka.ena(ena), // input wire ena.wea(wea), // input wire [0 : 0] wea.addra(addra), // input wire [6 : 0] addra.dina(dina), // input wire [7 : 0] dina.douta(douta) // output wire [7 : 0] douta
);
/b端口 存雙數
reg web ;
reg enb ;
reg [6:0] addrb ;
reg [7:0] dinb ;
wire [7:0] doutb;
//狀態機
///先寫再讀出
localparam RD_IDLE = 3'd4;
localparam RD_WRITE = 3'd5;
localparam RD_REG = 3'd6; ///緩沖狀態
localparam RD_READ = 3'd7;
reg [2:0] rd_cur_state,rd_next_state;
reg tx_start_b ;
always@(posedge sysclk)if(!rst_n)rd_cur_state <= RD_IDLE;elserd_cur_state <= rd_next_state;
always@(*)case(rd_cur_state)RD_IDLE : beginif(key_flag2)rd_next_state = RD_WRITE;elserd_next_state = rd_cur_state;end RD_WRITE :beginif(addrb >= 49)rd_next_state = RD_REG;elserd_next_state = rd_cur_state; endRD_REG :beginrd_next_state = RD_READ;endRD_READ :beginif(addrb >= 49)rd_next_state = RD_IDLE;elserd_next_state = rd_cur_state; enddefault:;endcase
always@(posedge sysclk)if(!rst_n)beginaddrb <= 0;web <= 0;enb <= 0;dinb <= 0;tx_start_b <= 0;endelsecase(rd_cur_state)RD_IDLE :beginaddrb <= 0;web <= 0;enb <= 0;dinb <= ram_even_data; ///020406endRD_WRITE :beginenb <= ~enb; web <= ~web;if(addrb >= 49)addrb <= 49;else if(web)addrb <= addrb + 1; dinb <= ram_even_data;endRD_REG :beginaddrb <= 0;enb <= 0;web <= 0;dinb <= 0;tx_start_b <= 1;endRD_READ :begin enb <= 1;web <= 0;dinb <= 0;if(tx_done)begintx_start_b <= 1;addrb <= addrb + 1;endelse begintx_start_b <= 0;addrb <= addrb;end enddefault:; endcase
blk_mem_gen_2 ram_b (.clka(sysclk), // input wire clka.ena(enb), // input wire ena.wea(web), // input wire [0 : 0] wea.addra(addrb), // input wire [6 : 0] addra.dina(dinb), // input wire [7 : 0] dina.douta(doutb) // output wire [7 : 0] douta
);
always@(posedge sysclk)if(!rst_n)uart_data <= 0;else if(cur_state == READ )uart_data <= douta ;else if(rd_cur_state == RD_READ )uart_data <= doutb ;elseuart_data <= uart_data;always@(posedge sysclk)if(!rst_n)tx_start <= 0;else if(cur_state == REG || cur_state == READ)tx_start <= tx_start_a;else if(rd_cur_state == RD_REG || rd_cur_state == RD_READ)tx_start <= tx_start_b;elsetx_start <= tx_start;endmodule