重点学习:
本实验重点学习了双口ram解决多bit跨时钟域同步处理的问题。
其实signal port ram,它的输入口和输出口分别用不同的时钟,也可以解决这个问题。
让我意识到的比较重要的事情:
1,代码设计中,一些大于10的数字,尽量用parameter设定一些可以重传的参数来代替。因为这样方便仿真修改。我就是之前写uart_rx模块,数字啥的没用参数替代。这个实验中仿真时需要修改参数,然后重新修改了uart_rx代码。使得仿真时间大大缩短,很方便。
2,在vga_pic模块中模拟产生的ram,读完数据后,ram里面的数据并没有消失,还在。
写数据:在时钟上升沿,写使能拉高,与此同时要有对应的地址与数据。然后数据就写进对应的地址里面了。
读数据:在时钟上升沿,读使能拉高,与此同时给出地址,然后数据就都出来了。值得一提的是,数据会滞后一个时钟周期(相对于读使能与地址),说明ram内部用的时序逻辑嘛。
3, task input ...如果写在task内部了。那么在task name 后面就不要加括号了。否则modulism会报错。
module vga_pic (input wire clk_25 , // ram 的输出时钟,也是vga图像的时钟。input wire clk_50 , // ram 的输入时钟input wire rst_n , // 经过系统复位和pll的locked相与后的复位。input wire [9:0] pix_x ,input wire [9:0] pix_y ,input wire [7:0] po_data , // 写入的数据的数据input wire po_flag , // 用作ram的读使能。output wire [7:0] pix_data
);// parameter parameter H_VALID = 10'd640 ,V_VALID = 10'd480 ;parameter PIC_SIZE= 14'd1_0000 ,H_PIC = 10'd100 ,V_PIC = 10'd100 ;parameter RED = 8'b1110_0000 ,GREEN = 8'b0001_1100 ,BLUE = 8'b0000_0011 ,WHITE = 8'b1111_1111 ,BLACK = 8'b0000_0000 ;// reg signal definereg [13:0] wr_addr ; // ram 的写地址。reg [ 7:0] data_pix ; // 彩条像素reg pic_valid; // 图片有效wire [ 7:0] pic_data ; // 图片像素reg rd_en ; // ram读使能reg [13:0] rd_addr ;/************************************************************************/// reg [13:0] wr_addr ; // ram 的写地址。 always @(posedge clk_50 or negedge rst_n) beginif(~rst_n) beginwr_addr <= 14'd0 ;end else beginif(po_flag) beginif(wr_addr == 9999) begin // 记到最大的地址后归零。wr_addr <= 14'd0 ;end else beginwr_addr <= wr_addr + 1'b1 ;endend else beginwr_addr <= wr_addr ;endendend// reg [ 7:0] data_pix ; // 彩条像素always @(posedge clk_25 or negedge rst_n) begin if(~rst_n) begindata_pix <= BLACK ;end else beginif(pix_y >= 0 && pix_y <= (V_VALID / 5 - 1))data_pix <= RED ;else if((pix_y >= V_VALID / 5) && pix_y <= ((V_VALID / 5 ) * 2) - 1)data_pix <= GREEN ;else if((pix_y >= (V_VALID / 5) * 2) && pix_y <= ((V_VALID / 5 ) * 3) - 1)data_pix <= BLUE ;else if((pix_y >= (V_VALID / 5) * 3) && pix_y <= ((V_VALID / 5 ) * 4) - 1)data_pix <= WHITE ;else if((pix_y >= (V_VALID / 5) * 4) && pix_y <= (V_VALID - 1))data_pix <= BLACK ;else data_pix <= BLACK ;end // 先不写else 一会看编译是否会通过。end// reg pic_valid; // 图片有效always @(posedge clk_25 or negedge rst_n) beginif(~rst_n) beginpic_valid <= 1'b0 ;end else beginif(pix_x >= 269 && pix_x <= 368 && pix_y >= 190 && pix_y <= 289) beginpic_valid <= 1'b1 ;end else beginpic_valid <= 1'b0 ;endendend// wire [ 7:0] pic_data ; // 图片像素// 图片像素存在了ram中,所以只需要取出来就是图片像素了。// reg rd_en ; // ram读使能always @(posedge clk_25 or negedge rst_n) beginif(~rst_n) beginrd_en <= 1'b0 ;end else beginif(pix_x >= 268 && pix_x <= 367 && pix_y >= 190 && pix_y <= 289) beginrd_en <= 1'b1 ;end else beginrd_en <= 1'b0 ;endendend// reg [13:0] rd_addr ;always @(posedge clk_25 or negedge rst_n) beginif(~rst_n) beginrd_addr <= 14'd0 ;end else beginif(rd_en) beginif(rd_addr == 9999) beginrd_addr <= 14'd0 ;end else beginrd_addr <= rd_addr + 1'b1 ;endend else beginrd_addr <= rd_addr ;endendend/**********************output signal define**************************************/// wire [7:0] pix_data ;assign pix_data = (pic_valid == 1'b1) ? pic_data : data_pix ;
/******************例化双口ram*******************/
ram_10000_double ram_10000_double_insert(.data ( po_data ) ,.inclock ( clk_50 ) ,.outclock ( clk_25 ) ,.rdaddress ( rd_addr ) ,.wraddress ( wr_addr ) ,.wren ( po_flag ) ,.q ( pic_data)
);endmodule
module top(input wire sys_clk ,input wire sys_rst_n ,input wire rx ,output wire [7:0] rgb ,output wire hsync ,output wire vsync
);// 例化间连线wire rst_n ;wire [7:0] po_data ;wire po_flag ;wire [7:0] pix_data ;wire [9:0] pix_x ;wire [9:0] pix_y ;wire clk_25 ;wire clk_50 ;pll pll_inst (.sys_rst_n ( sys_rst_n ) ,.areset ( ~sys_rst_n ) ,.inclk0 ( sys_clk ) ,.c0 ( clk_25 ) ,.c1 ( clk_50 ) ,.locked ( rst_n )
);uart_rx uart_rx_inst(.sys_clk ( clk_50 ) ,.sys_rst_n ( rst_n ) ,.rx ( rx ) ,.po_data ( po_data ) ,.po_flag ( po_flag )
);vga_ctrl vga_ctrl_inst(.vga_clk ( clk_25 ) ,.vga_rst_n ( rst_n ) ,.pix_data ( pix_data ) ,.hsync ( hsync ) ,.vsync ( vsync ) ,.pix_x ( pix_x ) ,.pix_y ( pix_y ) ,.rgb ( rgb )
);vga_pic vga_pic_inst(.clk_25 ( clk_25 ) ,.clk_50 ( clk_50 ) ,.rst_n ( rst_n ) ,.pix_x ( pix_x ) ,.pix_y ( pix_y ) ,.po_data ( po_data ) ,.po_flag ( po_flag ) ,.pix_data ( pix_data )
);endmodule
`timescale 1ns/1ns
module test_top();reg sys_clk ;reg sys_rst_n ;reg rx ;wire [7:0] rgb ;wire hsync ;wire vsync ;reg [7:0] data_mem [9999:0];top top_insert(.sys_clk ( sys_clk ) ,.sys_rst_n ( sys_rst_n ) ,.rx ( rx ) ,.rgb ( rgb ) ,.hsync ( hsync ) ,.vsync ( vsync )
);defparam top_insert.uart_rx_inst.CLK_FREQ = 5 ;defparam top_insert.uart_rx_inst.UART_BPS = 1 ;parameter CYCLE = 20 ;initial beginsys_clk = 1'b1 ;sys_rst_n <=1'b0 ;#(CYCLE) ;sys_rst_n <=1'b1 ; endalways #(CYCLE / 2) sys_clk = ~sys_clk ;initial begin$readmemh ("D:/intel_FPGA/VScodedocument/EmbedFire/31 rs232_vga/matlab/data_test.txt",data_mem);endinitial beginrx <= 1'b1 ;#(CYCLE * 10) ;rx_byte ;endtask rx_byte;integer j ; // j 是存储器的地址。for (j = 0;j < 10000 ;j = j + 1 ) beginrx_bit(data_mem[j]) ; // 没有输入端口,自然不需要输入参数。endendtasktask rx_bit;input [7:0]data ;integer i ;for (i = 0;i < 10 ;i = i + 1 ) begincase (i)0:rx <= 1'b0;1:rx <= data[0];2:rx <= data[1];3:rx <= data[2];4:rx <= data[3];5:rx <= data[4];6:rx <= data[5];7:rx <= data[6];8:rx <= data[7];9:rx <= 1'b1;default: rx <= 1'b1;endcase#(5 * CYCLE) ;endendtask
endmodule