【问题标题】:Valid-Ready handshake in VerilogVerilog 中的有效准备握手
【发布时间】:2018-12-02 19:44:12
【问题描述】:

我正在尝试在 verilog 中学习有效/就绪的握手。特别是,我有兴趣使用 ready 作为指示数据事务成功的标志(即,ready_invalid_out 变高之后变高)。我想用一个非常简单的 Verilog 示例来解释我的问题。我写了一个卷积编码器(代码如下)

module Conv_Encoder_Core(
    input wire clk,                 
    input wire reset,        
    input wire in_bit,
    output reg out_A,
    output reg out_B,
    input wire sleep,          
    input wire valid_in,
    input wire ready_in,
    output reg valid_out,
    output reg ready_out);

reg [5:0] S;
wire S_A, S_B, clkON;
assign S_A = S[1] ^ S[2] ^ S[4] ^S[5];
assign S_B = S[0] ^ S[1] ^ S[2] ^S[5];
assign clkON = clk & !sleep;

always @(posedge clkON)begin
    if (reset) begin
        S <=0;
        valid_out <=0; 
        ready_out <=0;
    end else if (valid_in) begin
        out_A <= in_bit ^ S_A;
        out_B <= in_bit ^ S_B;
        valid_out <=1;
        if (ready_in)begin
            S<= S<<1;
            S[0] <=in_bit;
            ready_out <=1;
        end else begin
            ready_out <=0;
        end

    end else begin
        valid_out <=0;
        ready_out <=0;
    end


end 
endmodule

我有兴趣使用ready_in 标志作为指示数据out_Aout_B 被下一个块接收,因此我的块可以通过将ready_out 标志设置为高来接受新数据。我已经为此块编写了一个测试台,但是,我没有得到我期望的结果

`timescale 1 ns/1 ns
module TB_Conv();
reg  clk;
//---------------clock generator-----------------------
initial begin
    clk = 1'b0; 
    #5; 
    clk = 1'b1; 
    forever    begin
        #5 clk = ~clk;      
    end
end
//------------------ dump -----------------------
initial begin
    $dumpfile("dumpVCD.vcd");
    $dumpvars(10);  
end

localparam N_DATA=10;
reg in_bits_vec [0:N_DATA-1];
initial begin
    in_bits_vec[0] = 1'b1;
    in_bits_vec[1] = 1'b0; 
    in_bits_vec[2] = 1'b0; 
    in_bits_vec[3] = 1'b0; 
    in_bits_vec[4] = 1'b0;
    in_bits_vec[5] = 1'b0;
    in_bits_vec[6] = 1'b0;
    in_bits_vec[7] = 1'b0;
    in_bits_vec[8] = 1'b0;
    in_bits_vec[9] = 1'b1;
end
reg in_bit, ready_in,reset, valid_in;
Conv_Encoder_Core UUT(.clk(clk),
                        .reset(reset),
                        .in_bit(in_bit),
                        .out_A(out_A),
                        .out_B(out_B),
                        .sleep(1'b0),
                        .valid_in(valid_in),
                        .ready_in(ready_in),
                        .valid_out(valid_out),
                        .ready_out(ready_out));

//---------------- code starts here -------------------//
reg [3:0] addr;
always @(posedge clk) begin
    if (reset)begin
        addr<=0;
        valid_in <=0;
        in_bit <=0;
    end else if (addr < 10) begin
        in_bit <= in_bits_vec[addr];
        valid_in <=1'b1;
        if (ready_out) begin
            addr <= addr+1'b1;
        end

    end else  begin
        in_bit <=0;
        valid_in <=0;
    end

    if (valid_out==1) ready_in <= 1;
    else              ready_in <= 0;

end 
// ----------- reset logic -----------//
reg [3:0] cnt;
initial cnt=0;
always @(negedge clk)begin
    if (cnt<5) begin
        reset = 1;
        cnt=cnt+1;
    end else  reset =0;
end

initial begin
 #1000;
$finish;
end
endmodule

如果您查看输入数据(在 testbech 中),您可以看到它是 1000000000。我期待看到1 通过S 注册如下:

S = 000000 //at beginning
S = 000001 // after ready_out=1
S = 000010
S = 000100

但是,我得到的结果完全不同(请参阅快照)。 我遇到的另一个问题是inbit=1 比我预期的多两个时钟周期。事实上,当ready_out=1 时,我希望看到in_bit 变为零,但这会在两个时钟周期后发生(快照中的黄色光标)。

如果有人能解释我在这个例子中做错了什么,我将不胜感激。

【问题讨论】:

    标签: verilog system-verilog hdl


    【解决方案1】:

    Conv_Encoder_Core

    module Conv_Encoder_Core
    (
        input wire clk,
        input wire reset,
        input wire in_bit,
        output reg out_A,
        output reg out_B,
        input wire sleep,
        // input channel
        input  wire inp_valid_i,
        output wire inp_ready_o,
        // output channel
        output reg out_valid_o,
        input  reg out_ready_i
    );
    
    reg [5:0] S;
    wire S_A, S_B, clkON;
    assign S_A = S[1] ^ S[2] ^ S[4] ^S[5];
    assign S_B = S[0] ^ S[1] ^ S[2] ^S[5];
    assign clkON = clk & !sleep;
    
    
    // -- Changes start here -- //
    wire wr_en;
    reg full_r;
    
    assign wr_en = ~full_r | out_ready_i;
    always @(posedge clkON)begin
        if (reset) begin
            S <=0;
            full_r <=0;
        end else begin
            if (wr_en) begin
                if (inp_valid_i) begin
                    full_r  <= 1;
                    out_A   <= in_bit ^ S_A;
                    out_B   <= in_bit ^ S_B;
                    S       <= S<<1;
                    S[0]    <=in_bit;
                end else begin
                    full_r  <= 0;
                end
            end
        end
    end
    
    assign inp_ready_o = wr_en;
    assign out_valid_o = full_r;
    
    endmodule
    

    tb

    `timescale 1 ns/1 ns
    module tb();
    reg  clk;
    //---------------clock generator-----------------------
    initial begin
        clk = 1'b0; 
        #5; 
        clk = 1'b1; 
        forever    begin
            #5 clk = ~clk;      
        end
    end
    //------------------ dump -----------------------
    initial begin
        $dumpfile("dumpVCD.vcd");
        $dumpvars(10);  
    end
    
    localparam N_DATA=10;
    reg in_bits_vec [0:N_DATA-1];
    initial begin
        in_bits_vec[0] = 1'b1;
        in_bits_vec[1] = 1'b0; 
        in_bits_vec[2] = 1'b0; 
        in_bits_vec[3] = 1'b0; 
        in_bits_vec[4] = 1'b0;
        in_bits_vec[5] = 1'b0;
        in_bits_vec[6] = 1'b0;
        in_bits_vec[7] = 1'b0;
        in_bits_vec[8] = 1'b0;
        in_bits_vec[9] = 1'b1;
    end
    reg in_bit, reset, inp_valid, inp_ready, out_valid, out_ready;
    Conv_Encoder_Core UUT(.clk(clk),
                            .reset(reset),
                            .in_bit(in_bit),
                            .out_A(out_A),
                            .out_B(out_B),
                            .sleep(1'b0),
                            // input channel
                            .inp_valid_i(inp_valid),
                            .inp_ready_o(inp_ready),
                            // output channel
                            .out_valid_o(out_valid),
                            .out_ready_i(out_ready));
    
    //---------------- code starts here -------------------//
    reg [3:0] addr;
    
    // -- Transmitter Side -- //
    always @(posedge clk) begin: ff_addr
        if (reset)begin
            addr <= 0;
        end else begin
            if (addr < 10) begin
                if (inp_valid && inp_ready) begin
                    addr <= addr + 1;
                end
            end else begin
                addr <= 0;
            end
        end
    end
    
    assign inp_valid = (addr < 10) ? 1'b1 : 1'b0;
    
    assign in_bit = in_bits_vec[addr];
    
    // -- Receiver Side -- //
    always @(posedge clk) begin: ff_ready_in
        if (reset) begin
            out_ready <= 0;
        end else begin
            out_ready <= $urandom_range(0, 1); // some randomness on the receiver, otherwise, we won't see if our DUT behaves correctly in case of ready=0
        end
    end
    
    // ----------- reset logic -----------//
    reg [3:0] cnt;
    initial cnt=0;
    always @(negedge clk)begin
        if (cnt<5) begin
            reset = 1;
            cnt=cnt+1;
        end else  reset =0;
    end
    
    initial begin
     #1000;
    $finish;
    end
    endmodule
    

    您的实施问题

    错误的协议定义和实现

    您正在定义一个看起来更像“请求/确认”而不是“就绪/有效”的协议,因为您的协议中的数据传输在一个周期延迟后得到确认。你需要的是同一个周期的并发传输确认,如下所示:

    发送器通过valid=1 指示有效的数据传输,并由接收器通过ready=1 确认。所以,只有valid &amp;&amp; ready在同一个周期内,一次数据传输才有效。请注意,在您的情况下,输入 data 等效于 in_bit,而输出 dataout_Aout_B

    输入/输出就绪/有效通道混淆

    如果你在上述通道的 Transmitter 和 Receiver 之间添加一个处理/缓冲单元,那么你得到的是这样的:

    在这种情况下,您的缓冲区是 Conv_Encoder_Core 模块,除了其内部核心逻辑之外,它还必须公开一个 input 就绪/有效通道,从该通道接收输入数据,以及一个输出 一个,从中输出数据。另请注意,发送器和接收器由测试台代码(tb 模块)实现。看 代码中的“发送方”和“接收方”cmets。

    【讨论】:

      猜你喜欢
      • 2014-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-23
      • 2016-07-28
      • 1970-01-01
      • 2019-07-27
      • 2017-07-17
      相关资源
      最近更新 更多