FPGA练习题记录

该文详细介绍了使用Verilog实现各种数字逻辑功能,包括奇数分频电路、串行并行转换、乘法运算、序列检测、跨时钟域数据同步、边沿检测以及FIFO设计。每个模块都通过测试文件进行了仿真验证,确保了功能的正确性。这些设计涵盖了数字逻辑设计的基础与应用,展示了Verilog在硬件描述语言中的强大能力。
摘要由CSDN通过智能技术生成

1.奇数分频电路,占空比50%

// 实现任意奇数分频,占空比为50%

module div_clk 
# (parameter div_N = 3 , log_div_N = 2 , div_clk_half = 1)          // div_N表示几分频
(                                                                   // log_div_N表示比div_N大的最小2的整数次方的数的2的指数
    input logic clk,                                                //     例如比3大的最小的是4=2^2,所以log_div_N=2
    input logic rstn,                                               //     例如比5大的最小的是8=2^3,所以log_div_N=3
    output logic div_clk                                            // div_clk_half = (div_N - 1)/2  ,因为我们这都是奇数分频
);

logic [log_div_N-1:0] cnt;
logic pos_clk , neg_clk;

// cnt记录时钟周期,N分频就要每N个周期循环一次
always @(posedge clk or negedge rstn)begin
    if(!rstn)begin
        cnt <= 'd0;
    end
    else begin
        if(cnt == div_N - 1)
            cnt <= 'd0;
        else
            cnt <= cnt + 1'b1;
    end
end

// 对于N分频,我们让pos_clk成为周期为N,高电平持续 (N-1)/2 个周期的信号
always @(posedge clk or negedge rstn)begin
    if(!rstn)begin
        pos_clk <= 1'b0;
    end
    else begin
        if(cnt == 'd0 || cnt == div_clk_half)   // 在第0个周期和第(N-1)/2个周期改变,高电平持续 (N-1)/2 个周期的信号
            pos_clk <= ~pos_clk;
        else 
            pos_clk <= pos_clk;
    end
end

// 对于N分频,我们让neg_clk也成为周期为N,高电平持续(N-1)/2 个周期的信号
// 但是neg_clk为下降沿触发,则neg_clk和pos_clk错开半个周期,两者相或,则得到
// 占空比为50%的奇数N分频
always @(negedge clk or negedge rstn)begin
    if(!rstn)begin
       neg_clk <= 1'b0;
    end
    else begin
        if(cnt == 'd1 || cnt == div_clk_half + 1'b1)        // 因为错开了半个周期,所以在第0+1个周期和第((N-1)/2)+1个周期改变,高电平持续 (N-1)/2 个周期的信号
            neg_clk <= ~neg_clk;
        else 
            neg_clk <= neg_clk;
    end
end

// 最后两个周期和占空比相同,但相位相差半个周期的信号相或,得到了占空比为50%的N分频时钟信号
assign div_clk = pos_clk | neg_clk;

endmodule

测试文件

`timescale 1ns/1ps
module tb ();

    initial begin
	$dumpfile("wave.vcd");  			// 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
	//$dumpfile("./simulation/wave.vcd");  	// wave.vcd文件将存储在当前目录下的simulation文件夹下
	$dumpvars(0, tb );  				// 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
	#600 $finish;						// 一定要设置一个仿真停止时间
    end


    logic clk,rstn,div_clk;

    initial begin
        clk = 0;
        forever begin
             #10 clk = ~clk;
        end
    end

    initial begin
        rstn = 1;
        #15 rstn = 0;
        #10 rstn = 1; 
    end

div_clk#(
    .div_N           ( 7 ),
    .log_div_N       ( 3 ),
    .div_clk_half    ( 3 )
)u_div_clk(
    .clk      ( clk      ),
    .rstn     ( rstn     ),
    .div_clk  ( div_clk  )
);


endmodule

仿真波形
在这里插入图片描述

2.串并转换

module serial_parallel_conv
#(parameter WIDTH = 8 , LOG2_WIDTH = 3)
 (
    input   logic                       clk,
    input   logic                       rstn,
    input   logic                       i_data,
    output  logic   [WIDTH - 1 : 0]     o_data
);

logic [LOG2_WIDTH: 0] cnt;
logic [WIDTH - 1 : 0] data_p;


always @(posedge clk or negedge rstn ) begin
    if (!rstn) begin
        cnt <= 'd0;
    end
    else begin
        if(cnt == WIDTH-1)begin
            cnt <= 'd0;
        end
        else begin
            cnt <= cnt + 1'b1;
        end
    end
end


// MSB优先:串行通信先发送最高有效位,存储在高位
always @(posedge clk or negedge rstn ) begin
    if (!rstn) begin
        data_p <= 'd0;
    end
    else begin
        data_p <= {data_p[WIDTH - 2 : 0],i_data};
    end
end

// // LSB优先:串行通信先发送最低有效位,存储在低位
// always @(posedge clk or negedge rstn ) begin
//     if (!rstn) begin
//         cnt <= 'd0;
//         data_p <= 'd0;
//     end
//     else begin
//         cnt <= cnt + 1'b1;
//         data_p <= {i_data , data_p[WIDTH - 1 : 1]}
//     end
// end

// 输出比输入结束时晚了一个时钟周期
// always @(posedge clk or negedge rstn) begin
//     if(!rstn)begin
//         o_data <= 'd0;
//     end
//     else begin
//         o_data <= (cnt == 0) ? data_p : 'd0;
//     end
// end 

// 在输入结束时立即输出
assign o_data = (cnt == 0) ? data_p : 'd0;

    
endmodule

测试文件

`timescale 1ns/1ps
module tb();


    // iverilog -g2005-sv -o sim.out tb.sv serial_parallel_conv.sv
    // vvp -n sim.out
    // gtkwave wave.vcd


    initial begin
	$dumpfile("wave.vcd");  			// 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
	//$dumpfile("./simulation/wave.vcd");  	// wave.vcd文件将存储在当前目录下的simulation文件夹下
	$dumpvars(0, tb );  				// 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
	#1000 $finish;						// 一定要设置一个仿真停止时间
    end

    logic clk,rstn,i_data;
    logic [7:0] o_data;

    initial begin
        clk = 0;
        forever begin
            #10 clk = ~clk;
        end
    end

    initial begin
        rstn = 1;
        #15 rstn = 0;
        #10 rstn = 1;
        #500 rstn = 0;
        #15 rstn = 1;
    end

    initial begin
        i_data = 1'd0;
        #28 i_data = 1'b1;
        #20 i_data = 1'b1;
        #20 i_data = 1'b0;
        #20 i_data = 1'b0;
        #20 i_data = 1'b1;
        #20 i_data = 1'b0;
        #20 i_data = 1'b1;
        #20 i_data = 1'b0;
        #20 i_data = 1'b1;
    end

serial_parallel_conv#(
    .WIDTH         ( 8 ),
    .LOG2_WIDTH    ( 3 )
)u_serial_parallel_conv(
    .clk     ( clk     ),
    .rstn    ( rstn    ),
    .i_data  ( i_data ),
    .o_data  ( o_data  )
);

endmodule

仿真波形

在这里插入图片描述

3.用verilog实现乘法y = a * b ,a和b都是8bit,考虑三种情况:

  1. 都是无符号数

  2. 都是有符号数

  3. a是有符号数,b是无符号数

module t1(
    input       [ 7:0]  i_a     ,		//无符号数a
    input       [ 7:0]  i_b     ,		//无符号数b
    output      [15:0]  o_y					//乘法结果的位宽:a的位宽+b的位宽+0个符号位
    );
assign o_y = i_a * i_b;  // 位宽为8+8+0
endmodule
module t2(
    input    signed   [ 7:0]  i_a     ,			//有符号数a
    input    signed   [ 7:0]  i_b     ,			//有符号数b
    output   signed   [14:0]  o_y					//乘法结果的位宽:a除去符号位的位宽+b除去符号位的位宽+1个符号位
    );
 
    assign o_y = i_a * i_b;		//位宽为7+7+1
endmodule
3)module t3(
    input    signed   [ 7:0]  i_a     ,		//有符号数a
    input             [ 7:0]  i_b     ,		//无符号数b
    output   signed   [15:0]  o_y				//乘法结果的位宽:a除去符号位的位宽+b的位宽+1个符号位
    );
    wire    signed  [8:0]   b_r;
    assign b_r = {1'b0,i_b};//加0不会出错,因为正数的补码与原码相同
 
    assign o_y = i_a * b_r;//7+8+1
endmodule

4.画出SRAM bit cell结构图

SRAM存储的原理就是基于双稳态电路,将两个反相器的输入与输出分别交叉连接,然后再利用两个MOS管作为字线进行选通,总共需要6个MOS管
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.序列检测1101

带重叠检测

module FSM_seq1101 (
    input   logic   clk,
    input   logic   rstn,
    input   logic   i_data,
    output  logic   o_out
);

parameter IDLE = 4'b0000 , S1 = 4'B0001 , S2 = 4'B0010 , S3 = 4'b0100, S4 = 4'b1000;
logic [3:0] state , next_state;

always@(*)begin
    case(state)
        IDLE:   next_state <= i_data ? S1: IDLE;
        S1:     next_state <= i_data ? S2: IDLE;
        S2:     next_state <= i_data ? S2: S3;
        S3:     next_state <= i_data ? S4: IDLE;
        S4:     next_state <= i_data ? S2: IDLE;
        default:   next_state <= IDLE;
    endcase
end

always@(posedge clk or negedge rstn)begin
    if(!rstn)begin
        state <= IDLE;
    end
    else begin
        state <= next_state;
    end
end

assign o_out = (state == S4)? 1'b1:1'b0;
    
endmodule

测试文件

`timescale 1ns/1ps
module tb();


    // iverilog -g2005-sv -o sim.out tb.sv FSM_seq1101.sv
    // vvp -n sim.out
    // gtkwave wave.vcd


    initial begin
	$dumpfile("wave.vcd");  			// 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
	//$dumpfile("./simulation/wave.vcd");  	// wave.vcd文件将存储在当前目录下的simulation文件夹下
	$dumpvars(0, tb );  				// 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
	#1000 $finish;						// 一定要设置一个仿真停止时间
    end

    logic clk,rstn,i_data,o_out;

    initial begin
        clk = 0;
        forever begin
            #10 clk = ~clk;
        end
    end

    initial begin
        rstn = 1;
        #15 rstn = 0;
        #10 rstn = 1;
        #500 rstn = 0;
        #15 rstn = 1;
    end

    initial begin
        i_data = 1'd0;
        #28 i_data = 1'b1;
        #20 i_data = 1'b1;
        #20 i_data = 1'b0;
        #20 i_data = 1'b0;
        #20 i_data = 1'b1;
        #20 i_data = 1'b1;
        #20 i_data = 1'b1;
        #20 i_data = 1'b0;
        #20 i_data = 1'b1;
        #20 i_data = 1'b1;
        #20 i_data = 1'b0;
        #20 i_data = 1'b1;
    end

FSM_seq1101 u_FSM_seq1101(
    .clk    ( clk    ),
    .rstn   ( rstn   ),
    .i_data ( i_data ),
    .o_out  ( o_out  )
);


endmodule

仿真波形
在这里插入图片描述

6.单bit跨时钟域处理

clk_a时钟域的信号同步到clk_b时钟域,分为几种情况:
(1)慢时钟到快时钟
(2)快时钟到慢时钟


(1)慢时钟到快时钟

// 单bit跨时钟域,慢时钟到快时钟

module onebit_sync_f2s (
    input   logic   clk_a,
    input   logic   clk_b,
    input   logic   rstn,
    input   logic   i_data,
    output  logic   o_out
);
logic   a_sig;
logic   [1:0]   b_sig;

// 在clk_a时钟域下打一拍
always @(posedge clk_a or negedge rstn) begin
    if(!rstn)begin
        a_sig <= 1'b0;
    end
    else begin
        a_sig <= i_data;
    end
end

// 在clk_b时钟域下打两拍,防止亚稳态
always@(posedge clk_b or negedge rstn) begin
    if(!rstn)begin
        b_sig <= 2'b0;
    end
    else begin
        b_sig <= {b_sig[0],a_sig};
    end
end

assign o_out = b_sig[1];

endmodule

测试文件

`timescale 1ns/1ps
module tb();


    // iverilog -g2005-sv -o sim.out tb.sv onebit_sync_f2s.sv
    // vvp -n sim.out
    // gtkwave wave.vcd


    initial begin
	$dumpfile("wave.vcd");  			// 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
	//$dumpfile("./simulation/wave.vcd");  	// wave.vcd文件将存储在当前目录下的simulation文件夹下
	$dumpvars(0, tb );  				// 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
	#1000 $finish;						// 一定要设置一个仿真停止时间
    end

    logic clk_a,clk_b,rstn,i_data,o_out;

    initial begin
        clk_a = 0;
        forever begin
            #10 clk_a = ~clk_a;
        end
    end

    initial begin
        clk_b = 0;
        forever begin
            #4 clk_b = ~clk_b;
        end
    end

    initial begin
        rstn = 1;
        #15 rstn = 0;
        #10 rstn = 1;
        #500 rstn = 0;
        #15 rstn = 1;
    end

    initial begin
        i_data = 1'd0;
        #28 i_data = 1'b1;
        #20 i_data = 1'b1;
        #20 i_data = 1'b0;
        #20 i_data = 1'b0;
        #20 i_data = 1'b1;
        #20 i_data = 1'b1;
        #20 i_data = 1'b1;
        #20 i_data = 1'b0;
        #20 i_data = 1'b1;
        #20 i_data = 1'b1;
        #20 i_data = 1'b0;
        #20 i_data = 1'b1;
    end

onebit_sync_f2s u_onebit_sync_f2s(
    .clk_a  ( clk_a  ),
    .clk_b  ( clk_b  ),
    .rstn   ( rstn   ),
    .i_data ( i_data ),
    .o_out  ( o_out  )
);



endmodule

在这里插入图片描述


(2)快时钟到慢时钟
要将一个信号从快时钟域传递到慢时钟域,首先要在快时钟域展宽脉冲,然后将展宽的脉冲传递到慢时钟域,如果在慢时钟域检测到脉冲后就可以给快时钟域一个反馈以阻止脉冲的继续展宽;

module onebit_sync_s2f(
    input   logic   clk_a,
    input   logic   clk_b,
    input   logic   rstn,
    input   logic   i_data,
    output  logic   o_out
);

logic   a_sig,b_sig;
logic   [1:0]   b_sync,a_sync;

// 在clk_a下生成展宽信号a_sig
always @(posedge clk_a or negedge rstn) begin
    if(!rstn)begin
        a_sig <= 1'b0;
    end
    else begin
        if(i_data == 1'b1)begin
            a_sig = 1'b1;
        end
        else if(a_sync[1] == 1'b1)begin
            a_sig = 1'b0;
        end
        else begin
            a_sig = a_sig;
        end
    end
end

// 在clk_b下同步a_sig信号
always @(posedge clk_b or negedge rstn) begin
    if(!rstn)begin
        b_sig <= 1'b0;
    end
    else begin
        b_sig <= a_sig;
    end
end

// 在clk_b下打两拍,生成跨时钟域信号
always @(posedge clk_b or negedge rstn) begin
    if(!rstn)begin
        b_sync <= 2'b00;
    end
    else begin
        b_sync <= {b_sync[0],b_sig};
    end
end

assign o_out = b_sync[1];

// clk_b同步了信号后,生成一个信号使a_sig停止展宽
always @(posedge clk_a or negedge rstn) begin
    if(!rstn)begin
        a_sync <= 2'b00;
    end
    else begin
        a_sync <= {a_sync[0],b_sync[1]};
    end
end

endmodule

测试文件

`timescale 1ns/1ps
module tb();


    // iverilog -g2005-sv -o sim.out tb.sv onebit_sync_s2f.sv
    // vvp -n sim.out
    // gtkwave wave.vcd


    initial begin
	$dumpfile("wave.vcd");  			// 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
	//$dumpfile("./simulation/wave.vcd");  	// wave.vcd文件将存储在当前目录下的simulation文件夹下
	$dumpvars(0, tb );  				// 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
	#1000 $finish;						// 一定要设置一个仿真停止时间
    end

    logic clk_a,clk_b,rstn,i_data,o_out;

    initial begin
        clk_a = 0;
        forever begin
            #10 clk_a = ~clk_a;
        end
    end

    initial begin
        clk_b = 0;
        forever begin
            #16 clk_b = ~clk_b;
        end
    end

    initial begin
        rstn = 1;
        #15 rstn = 0;
        #10 rstn = 1;
        #500 rstn = 0;
        #15 rstn = 1;
    end

    initial begin
        i_data = 1'd0;
        #28 i_data = 1'b1;
        #20 i_data = 1'b0;
        // #20 i_data = 1'b0;
        // #20 i_data = 1'b0;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b0;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b0;
        // #20 i_data = 1'b1;
    end

onebit_sync_s2f u_onebit_sync_s2f(
    .clk_a  ( clk_a  ),
    .clk_b  ( clk_b  ),
    .rstn   ( rstn   ),
    .i_data ( i_data ),
    .o_out  ( o_out  )
);



endmodule

仿真波形
在这里插入图片描述7.边沿检测

// 信号边沿检测,采样频率要大于信号频率的两倍
module edge_detect (
    input logic clk,
    input logic rstn,
    input logic i_data,
    output logic pos_edge,    
    output logic neg_edge,    
    output logic data_edge
);

logic [1:0] data_r;

always @(posedge clk or negedge rstn) begin
    if(!rstn)begin
        data_r <= 2'b0;
    end
    else begin
        data_r <= {data_r[0],i_data};
    end
end

assign pos_edge = ~data_r[1] & data_r[0];
assign neg_edge = data_r[1] & ~data_r[0];
assign data_edge = pos_edge | neg_edge;

endmodule

测试文件

`timescale 1ns/1ps
module tb();


    // iverilog -g2005-sv -o sim.out tb.sv edge_detect.sv
    // vvp -n sim.out
    // gtkwave wave.vcd


    initial begin
	$dumpfile("wave.vcd");  			// 指定VCD波形文件的名字为wave.vcd,仿真信息将记录到此文件,wave.vcd文件将存储在当前目录下
	//$dumpfile("./simulation/wave.vcd");  	// wave.vcd文件将存储在当前目录下的simulation文件夹下
	$dumpvars(0, tb );  				// 指定层次数为0,则tb模块及其下面各层次的所有信号将被记录,我们需要所有信号都被记录
	#1000 $finish;						// 一定要设置一个仿真停止时间
    end

    logic clk,rstn,i_data,pos_edge,neg_edge,data_edge;

    initial begin
        clk = 0;
        forever begin
            #10 clk = ~clk;
        end
    end

    initial begin
        rstn = 1;
        #15 rstn = 0;
        #10 rstn = 1;
        #500 rstn = 0;
        #15 rstn = 1;
    end

    initial begin
        i_data = 1'd0;
        #28 i_data = 1'b1;
        #45 i_data = 1'b0;
        #45 i_data = 1'b1;
        #45 i_data = 1'b0;
        #45 i_data = 1'b1;
        #45 i_data = 1'b1;
        #45 i_data = 1'b0;
        // #20 i_data = 1'b0;
        // #20 i_data = 1'b0;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b0;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b1;
        // #20 i_data = 1'b0;
        // #20 i_data = 1'b1;
    end

edge_detect u_edge_detect(
    .clk       ( clk       ),
    .rstn      ( rstn      ),
    .i_data    ( i_data    ),
    .pos_edge  ( pos_edge  ),
    .neg_edge  ( neg_edge  ),
    .data_edge ( data_edge )
);

endmodule

仿真波形
在这里插入图片描述

8.异步fifo

// 异步FIFO,使用格雷码
module FIFO_async 
#(
    parameter DWIDTH = 8,
    parameter AWIDTH = 3,
    parameter DDEPTH = 8
)
(
    input  logic rstn,

    input  logic w_clk,
    input  logic w_en,
    input  logic [DWIDTH-1:0]    w_data,

    input  logic r_clk,
    input  logic r_en,
    output logic [DWIDTH-1:0]    r_data,

    output logic w_full,
    output logic r_empty
);

// 读写地址
logic [AWIDTH-1:0]  w_addr,r_addr;
// 读写指针,比读写地址多一位,用来表示是否在同一轮中
logic [AWIDTH:0]    w_ptr,r_ptr;
// 读写指针的格雷码
logic [AWIDTH:0]    w_ptr_gray,r_ptr_gray;
// 写指针格雷码同步到读时钟域,读指针格雷码同步到写时钟域
logic [AWIDTH:0]    w_ptr_gray_to_r [1:0];
logic [AWIDTH:0]    r_ptr_gray_to_w [1:0];
// 定义fifo的memory
logic [DWIDTH-1:0]  fifo_mem [DDEPTH-1:0];

//----------------------------------------------------------------------
// 写指针递增
always @(posedge w_clk or negedge rstn) begin
    if(!rstn)begin
        w_ptr <= 'd0;
    end
    else begin
        w_addr <= (w_en == 1'b1 & !w_full) ? (w_addr + 1'b1) : w_addr;
    end
end
// 将写指针转换为格雷码
assign w_ptr_gray = (w_addr>>1) ^ w_addr;
// 生成写地址
assign w_addr = w_ptr[AWIDTH-1:0];
//----------------------------------------------------------------------


//----------------------------------------------------------------------
// 读指针递增
always @(posedge w_clk or negedge rstn) begin
    if(!rstn)begin
        r_ptr <= 'd0;
    end
    else begin
        r_addr <= (r_en == 1'b1 & !r_empty) ? (r_addr + 1'b1) : r_addr;
    end
end
// 将读指针转换为格雷码
assign r_ptr_gray = (r_addr>>1) ^ r_addr;
// 生成读地址
assign r_addr = r_ptr[AWIDTH-1:0];
//----------------------------------------------------------------------


//----------------------------------------------------------------------
// 写指针格雷码同步到读时钟域 
always@(posedge r_clk or negedge rstn)begin
    if(!rstn)begin
        w_ptr_gray_to_r[0] <= 'd0;
        w_ptr_gray_to_r[1] <= 'd0; 
    end
    else begin
        w_ptr_gray_to_r = {w_ptr_gray_to_r[0] , w_ptr_gray};
    end
end
//----------------------------------------------------------------------


//----------------------------------------------------------------------
// 读指针格雷码同步到写时钟域 
always@(posedge r_clk or negedge rstn)begin
    if(!rstn)begin
        r_ptr_gray_to_w[0] <= 'd0;
        r_ptr_gray_to_w[1] <= 'd0; 
    end
    else begin
        r_ptr_gray_to_w = {r_ptr_gray_to_w[0] , r_ptr_gray};
    end
end
//----------------------------------------------------------------------


//----------------------------------------------------------------------
//判断写满信号,前两位都不同,后面位都相同
assign w_full  = (r_ptr_gray)==({~w_ptr_gray_to_r[1][AWIDTH-:2] , w_ptr_gray_to_r[1][AWIDTH-2:0]});
//判断读空信号,前两位都不同,后面位都相同
assign r_empty = (w_ptr_gray == r_ptr_gray_to_w[1]);
//----------------------------------------------------------------------


//----------------------------------------------------------------------
// 写入和读出memory
always@(posedge w_clk)begin
    fifo_mem[w_addr] <= ((w_en == 1'b1) & !w_full) ? w_data : fifo_mem[w_addr];
end

always@(posedge r_clk)begin
    r_data <= ((r_en == 1'b1) & !r_empty) ? fifo_mem[r_addr] : r_data;
end
//----------------------------------------------------------------------
endmodule

9.同步fifo

//单口RAM实现同步fifo

module singleRAM_fifo#(
    parameter   DSIZE = 32,
    parameter   ASIZE = 3,
    parameter   DDEPTH = 8
)
(
    input   logic   clk,
    input   logic   rstn,

    input   logic   wen,
    input   logic   [DSIZE-1:0] wdata,
    input   logic   ren,
    output  logic   [DSIZE-1:0] rdata,

    output  logic   rempty,
    output  logic   wfull
);
// 读写指针
logic   [ASIZE:0]   wptr,rptr;
// 读写地址
logic   [ASIZE-1:0] waddr,raddr;
// RAM地址输入
logic   [ASIZE-1:0] RAM_i_addr;
// RAM数据输入
logic   [DSIZE-1:0] RAM_i_data;

// 写指针递增
always_ff@(posedge clk or negedge rstn)begin
    if(!rstn)begin
        wptr <= 0;
    end
    else if(wen & !wfull)begin
        wptr <= wptr + 1'b1;
    end
    else begin
        wptr <= wptr;
    end
end

// 读指针递增
always_ff@(posedge clk or negedge rstn)begin
    if(!rstn)begin
        rptr <= 0;
    end
    else if(ren & !rempty)begin
        rptr <= rptr + 1'b1;
    end
    else begin
        rptr <= rptr;
    end
end

// 写地址
assign waddr = wptr[ASIZE-1:0];
// 读地址
assign raddr = rptr[ASIZE-1:0];
// 读空信号
assign rempty = wptr == rptr;
// 写满信号
assign wfull = (wptr[ASIZE-1:0] == rptr[ASIZE-1:0]) & (wptr[ASIZE]^rptr[ASIZE]);

// RAM地址信号
assign RAM_i_addr = wen?wdata:rdata;

singleRAM RAM1(
    .clk(clk),
    .i_data(wdata),
    .i_addr(RAM_i_addr),
    .oe(ren & !rempty),
    .wr(wen & !wfull),
    .o_data(rdata)
);

endmodule




module singleRAM #(
    parameter   DSIZE = 32,
    parameter   ASIZE = 3,
    parameter   DDEPTH = 8
)
(
    input   logic   clk,

    input   logic   [DSIZE-1:0]     i_data,
    input   logic   [ASIZE-1:0]     i_addr,
    input   logic                   oe,             // 允许输出线
    input   logic                   wr,             // 写使能线

    output  logic   [DSIZE-1:0]  o_data
);

logic [DSIZE-1:0]   mem     [DDEPTH-1:0];

assign o_data = oe?32'bz:mem[i_addr];

always_ff@(posedge clk )begin
    if(wr)
        mem[i_addr] <= i_data;
end

endmodule

10.如图
在这里插入图片描述将两个乘法器减少为1个,同时数据选择器多用了1个;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值