第46讲:FIFO求和实验
理论部分
设计与实现
uart_rx
`timescale 1ns/1ns module uart_rx #( parameter UART_BPS = 'd9600, //串口波特率 parameter CLK_FREQ = 'd50_000_000 //时钟频率 ) ( input wire sys_clk , //系统时钟50MHz input wire sys_rst_n , //全局复位 input wire rx , //串口接收数据 output reg [7:0] po_data , //串转并后的8bit数据 output reg po_flag //串转并后的数据有效标志信号 ); //localparam define localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ; //reg define reg rx_reg1 ; reg rx_reg2 ; reg rx_reg3 ; reg start_nedge ; reg work_en ; reg [12:0] baud_cnt ; reg bit_flag ; reg [3:0] bit_cnt ; reg [7:0] rx_data ; reg rx_flag ; //插入两级寄存器进行数据同步,用来消除亚稳态 //rx_reg1:第一级寄存器,寄存器空闲状态复位为1 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_reg1 <= 1'b1; else rx_reg1 <= rx; //rx_reg2:第二级寄存器,寄存器空闲状态复位为1 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_reg2 <= 1'b1; else rx_reg2 <= rx_reg1; //rx_reg3:第三级寄存器和第二级寄存器共同构成下降沿检测 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_reg3 <= 1'b1; else rx_reg3 <= rx_reg2; //start_nedge:检测到下降沿时start_nedge产生一个时钟的高电平 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) start_nedge <= 1'b0; else if((~rx_reg2) && (rx_reg3)) start_nedge <= 1'b1; else start_nedge <= 1'b0; //work_en:接收数据工作使能信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) work_en <= 1'b0; else if(start_nedge == 1'b1) work_en <= 1'b1; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) work_en <= 1'b0; //baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) baud_cnt <= 13'b0; else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) baud_cnt <= 13'b0; else if(work_en == 1'b1) baud_cnt <= baud_cnt + 1'b1; //bit_flag:当baud_cnt计数器计数到中间数时采样的数据最稳定, //此时拉高一个标志信号表示数据可以被取走 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_flag <= 1'b0; else if(baud_cnt == BAUD_CNT_MAX/2 - 1) bit_flag <= 1'b1; else bit_flag <= 1'b0; //bit_cnt:有效数据个数计数器,当8个有效数据(不含起始位和停止位) //都接收完成后计数器清零 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_cnt <= 4'b0; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) bit_cnt <= 4'b0; else if(bit_flag ==1'b1) bit_cnt <= bit_cnt + 1'b1; //rx_data:输入数据进行移位 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_data <= 8'b0; else if((bit_cnt >= 4'd1)&&(bit_cnt <= 4'd8)&&(bit_flag == 1'b1)) rx_data <= {rx_reg3, rx_data[7:1]}; //rx_flag:输入数据移位完成时rx_flag拉高一个时钟的高电平 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) rx_flag <= 1'b0; else if((bit_cnt == 4'd8) && (bit_flag == 1'b1)) rx_flag <= 1'b1; else rx_flag <= 1'b0; //po_data:输出完整的8位有效数据 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) po_data <= 8'b0; else if(rx_flag == 1'b1) po_data <= rx_data; //po_flag:输出数据有效标志(比rx_flag延后一个时钟周期,为了和po_data同步) always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) po_flag <= 1'b0; else po_flag <= rx_flag; endmodule
uart_tx
`timescale 1ns/1ns module uart_tx #( parameter UART_BPS = 'd9600, //串口波特率 parameter CLK_FREQ = 'd50_000_000 //时钟频率 ) ( input wire sys_clk , //系统时钟50MHz input wire sys_rst_n , //全局复位 input wire [7:0] pi_data , //模块输入的8bit数据 input wire pi_flag , //并行数据有效标志信号 output reg tx //串转并后的1bit数据 ); //localparam define localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS ; //reg define reg [12:0] baud_cnt; reg bit_flag; reg [3:0] bit_cnt ; reg work_en ; //work_en:接收数据工作使能信号 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) work_en <= 1'b0; else if(pi_flag == 1'b1) work_en <= 1'b1; else if((bit_flag == 1'b1) && (bit_cnt == 4'd9)) work_en <= 1'b0; //baud_cnt:波特率计数器计数,从0计数到BAUD_CNT_MAX - 1 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) baud_cnt <= 13'b0; else if((baud_cnt == BAUD_CNT_MAX - 1) || (work_en == 1'b0)) baud_cnt <= 13'b0; else if(work_en == 1'b1) baud_cnt <= baud_cnt + 1'b1; //bit_flag:当baud_cnt计数器计数到1时让bit_flag拉高一个时钟的高电平 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_flag <= 1'b0; else if(baud_cnt == 13'd1) bit_flag <= 1'b1; else bit_flag <= 1'b0; //bit_cnt:数据位数个数计数,10个有效数据(含起始位和停止位)到来后计数器清零 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) bit_cnt <= 4'b0; else if((bit_flag == 1'b1) && (bit_cnt == 4'd9)) bit_cnt <= 4'b0; else if((bit_flag == 1'b1) && (work_en == 1'b1)) bit_cnt <= bit_cnt + 1'b1; //tx:输出数据在满足rs232协议(起始位为0,停止位为1)的情况下一位一位输出 always@(posedge sys_clk or negedge sys_rst_n) if(sys_rst_n == 1'b0) tx <= 1'b1; //空闲状态时为高电平 else if(bit_flag == 1'b1) case(bit_cnt) 0 : tx <= 1'b0; 1 : tx <= pi_data[0]; 2 : tx <= pi_data[1]; 3 : tx <= pi_data[2]; 4 : tx <= pi_data[3]; 5 : tx <= pi_data[4]; 6 : tx <= pi_data[5]; 7 : tx <= pi_data[6]; 8 : tx <= pi_data[7]; 9 : tx <= 1'b1; default : tx <= 1'b1; endcase endmodule
fifo_sum_ctrl
`timescale 1ns/1ns module fifo_sum_ctrl ( input wire sys_clk , //频率为50MHz input wire sys_rst_n , //复位信号,低有效 input wire [7:0] pi_data , //rx传入的数据信号 input wire pi_flag , //rx传入的标志信号 output reg [7:0] po_sum , //求和运算后的信号 output reg po_flag //输出数据标志信号 ); //parameter define parameter CNT_ROW_MAX = 7'd49 , //行计数最大值 CNT_COL_MAX = 7'd49 ; //列计数最大值 //wire define wire [7:0] data_out1 ; //fifo1数据输出 wire [7:0] data_out2 ; //fifo2数据输出 //reg define reg [6:0] cnt_row ; //行计数 reg [6:0] cnt_col ; //场计数 reg wr_en1 ; //fifo1写使能 reg wr_en2 ; //fifo2写使能 reg [7:0] data_in1 ; //fifo1写数据输入 reg [7:0] data_in2 ; //fifo2写数据输入 reg rd_en ; //fifo1、fifo2共用的读使能 reg dout_flag ; //控制fifo1,2-84行的写使能 reg po_flag_reg ; //输出标志位缓存,rd_en延后一拍得到,控制计算po_sum //cnt_row:行计数器,计数一行数据个数 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) cnt_row <= 7'd0; else if((cnt_row == CNT_ROW_MAX) && (pi_flag == 1'b1)) cnt_row <= 7'd0; else if(pi_flag == 1'b1) cnt_row <= cnt_row + 1'b1; end //cnt_col:列计数器,计数数据行数 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) cnt_col <= 7'd0; else if((cnt_col == CNT_COL_MAX) && (pi_flag == 1'b1) && (cnt_row == CNT_ROW_MAX)) cnt_col <= 7'd0; else if((cnt_row == CNT_ROW_MAX) && (pi_flag == 1'b1)) cnt_col <= cnt_col + 1'b1; end //wr_en1:fifo1写使能信号,高电平有效 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) wr_en1 <= 1'b0; else if((cnt_col == 7'd0) && (pi_flag == 1'b1)) wr_en1 <= 1'b1; //第0行写入fifo1 else wr_en1 <= dout_flag; //2-84行写入fifo1 end //wr_en2:fifo2写使能信号,高电平有效 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) wr_en2 <= 1'b0; else if((cnt_col >= 7'd1) && (cnt_col <= CNT_COL_MAX - 1'b1) && (pi_flag == 1'b1)) wr_en2 <= 1'b1; //2-CNT_COL_MAX行写入fifo2 else wr_en2 <= 1'b0; end //data_in1:fifo1数据输入 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) data_in1 <= 8'b0; else if((pi_flag == 1'b1) && (cnt_col == 7'd0)) data_in1 <= pi_data; //第0行数据暂存fifo1中 else if(dout_flag == 1'b1) data_in1 <= data_out2;//第2-CNT_COL_MAX-1行时,fifo2读出数据存入fifo1 else data_in1 <= data_in1; end //data_in2:fifo2数据输入 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) data_in2 <= 8'b0; else if((pi_flag == 1'b1)&&(cnt_col >= 7'd1)&&(cnt_col <= (CNT_COL_MAX - 1'b1))) data_in2 <= pi_data; else data_in2 <= data_in2; end //rd_en:fifo1和fifo2的共用读使能信号 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) rd_en <= 1'b0; else if((pi_flag == 1'b1)&&(cnt_col >= 7'd2)&&(cnt_col <= CNT_COL_MAX)) rd_en <= 1'b1; else rd_en <= 1'b0; end //dout_flag:控制2-CNT_COL_MAX-1行wr_en1信号 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) dout_flag <= 0; else if((wr_en2 == 1'b1) && (rd_en == 1'b1)) dout_flag <= 1'b1; else dout_flag <= 1'b0; end //po_flag_reg:输出标志位缓存,延后rd_en一拍,控制po_sum信号 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) po_flag_reg <= 1'b0; else if(rd_en == 1'b1) po_flag_reg <= 1'b1; else po_flag_reg <= 1'b0; end //po_flag:输出标志信号,延后输出标志位缓存一拍,与po_sum同步输出 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) po_flag <= 1'b0; else po_flag <= po_flag_reg; end //po_sum:求和数据输出 always@(posedge sys_clk or negedge sys_rst_n) begin if(sys_rst_n == 1'b0) po_sum <= 8'b0; else if(po_flag_reg == 1'b1) po_sum <= data_out1 + data_out2 + pi_data; else po_sum <= po_sum; end //------------- fifo_data_inst1 -------------- fifo_data fifo_data_inst1 ( .clock (sys_clk ), //input clock .data (data_in1 ), //input [7:0] data .wrreq (wr_en1 ), //input wrreq .rdreq (rd_en ), //input rdreq .q (data_out1 ) //output [7:0] q ); //------------- fifo_data_inst2 -------------- fifo_data fifo_data_inst2 ( .clock (sys_clk ), //input clock .data (data_in2 ), //input [7:0] data .wrreq (wr_en2 ), //input wrreq .rdreq (rd_en ), //input rdreq .q (data_out2 ) //output [7:0] q ); endmodule
fifo_sum
`timescale 1ns/1ns module fifo_sum ( input wire sys_clk , //输入系统时钟,50MHz input wire sys_rst_n , //复位信号,低电平有效 input wire rx , //串口数据接收 output wire tx //串口数据发送 ); //parameter define parameter UART_BPS = 14'd9600 , //比特率 CLK_FREQ = 26'd50_000_000 ; //时钟频率 //wire define wire [7:0] pi_data ; //输入待求和数据 wire pi_flag ; //输入数据标志信号 wire [7:0] po_sum ; //输出求和后数据 wire po_flag ; //输出数据标志信号 //------------- uart_rx_inst -------------- uart_rx #( .UART_BPS (UART_BPS ), //串口波特率 .CLK_FREQ (CLK_FREQ ) //时钟频率 ) uart_rx_inst ( .sys_clk (sys_clk ), //系统时钟50Mhz .sys_rst_n (sys_rst_n ), //全局复位 .rx (rx ), //串口接收数据 .po_data (pi_data ), //串转并后的数据 .po_flag (pi_flag ) //串转并后的数据有效标志信号 ); //------------- fifo_sum_ctrl_inst -------------- fifo_sum_ctrl fifo_sum_ctrl_inst ( .sys_clk (sys_clk ), //频率为50MHz .sys_rst_n (sys_rst_n ), //复位信号,低有效 .pi_data (pi_data ), //rx传入的数据信号 .pi_flag (pi_flag ), //rx传入的标志信号 .po_sum (po_sum ), //求和运算后的信号 .po_flag (po_flag ) //输出数据标志信号 ); //------------- uart_tx_inst -------------- uart_tx #( .UART_BPS (UART_BPS ), //串口波特率 .CLK_FREQ (CLK_FREQ ) //时钟频率 ) uart_tx_inst ( .sys_clk (sys_clk ), //系统时钟50Mhz .sys_rst_n (sys_rst_n ), //全局复位 .pi_data (po_sum ), //并行数据 .pi_flag (po_flag ), //并行数据有效标志信号 .tx (tx ) //串口发送数据 ); endmodule
tb_fifo_sum
`timescale 1ns/1ns module tb_fifo_sum(); //wire define wire tx ; //reg define reg clk ; reg rst_n ; reg rx ; reg [7:0] data_men[2499:0] ; //读取数据 initial $readmemh("E:/sources/fifo_sum/matlab/fifo_data.txt",data_men); //生成时钟和复位信号 initial begin clk = 1'b1; rst_n <= 1'b0; #30 rst_n <= 1'b1; end always #10 clk = ~clk; //rx赋初值,调用rx_byte initial begin rx <= 1'b1; #200 rx_byte(); end //rx_byte task rx_byte(); integer j; for(j=0;j<2500;j=j+1) rx_bit(data_men[j]); endtask //rx_bit task rx_bit(input[7:0] data);//data是data_men[j]的值。 integer i; for(i=0;i<10;i=i+1) begin case(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]; //上面8个发送的是数据位 9: rx <= 1'b1; //停止位 endcase #1040; end endtask //重定义defparam,用于修改参数 defparam fifo_sum_inst.uart_rx_inst.CLK_FREQ = 500000 ; defparam fifo_sum_inst.uart_tx_inst.CLK_FREQ = 500000 ; //------------- fifo_sum_inst -------------- fifo_sum fifo_sum_inst ( .sys_clk (clk ), .sys_rst_n (rst_n ), .rx (rx ), .tx (tx ) ); endmodule
FPGA强化(10):基于Sobel算法的边缘检测(二)+https://developer.aliyun.com/article/1556601