Verilog中的有效就绪握手

时间:2018-12-02 19:44:13

标签: verilog system-verilog hdl

我正在尝试在Verilog中学习有效/就绪的握手。我特别希望将ready用作表示成功进行数据交易的标志(即ready_in变高后valid_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的指示符,因此我的块可以通过设置{{1 }}标记为高。我已经为此块编写了一个测试平台,但是,没有得到预期的结果

ready_out

如果查看输入数据(在testbech中),则可以看到它是`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 。我期望看到1000000000通过1注册的过程如下:

S

但是,我得到的结果是完全不同的(请参见快照)。 我遇到的另一个问题是S = 000000 //at beginning S = 000001 // after ready_out=1 S = 000010 S = 000100 比我预期的要多两个时钟周期。实际上,当inbit=1出现时,我希望看到ready_out=1变为零,但这发生在两个时钟周期之后(快照中的黄色光标)。

如果有人可以解释我在此示例中做错的事情,我将不胜感激。

enter image description here

1 个答案:

答案 0 :(得分: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

问题与您的实施有关

错误的协议定义和实施

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

enter image description here

发送方通过valid=1指示有效的数据传输,接收方通过ready=1确认有效的数据传输。因此,仅当valid && ready处于同一周期时,数据传输才有效。请注意,在您的情况下,输入data等效于in_bit,而输出dataout_Aout_B

输入/输出准备就绪/有效的频道混乱

如果您在上述通道的发送器和接收器之间添加处理/缓冲单元,那么您将得到如下信息:

enter image description here

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