在 Vivado 仿真中立即分配非阻塞赋值

哦切罗

在实现cordic 算法时,我的非阻塞赋值立即分配变量,而不是在一个时钟周期后。我不知道为什么。我的代码或 Vivado 设计套件有问题吗?在我的代码中,我需要z[0]使用z[0] <= angle. 但是,在我的 Vivado 模拟器中,z[0]只是与angle.

环境:Windows10,Vivado 2020.2默认模拟器

`timescale 1ns/10ps

module cordic_tb #(

)(

);
    localparam real PI = 3.1415926535;
    logic clk,rst_n,en;
    
    logic signed [31:0] step=integer'((3/180.0)*(2.0**31.0));
    logic signed [31:0] angle;
    logic signed [31:0] cosine,sine;
    initial begin
        clk=0;
        en=1;
        forever #50 clk=~clk;
    end
    initial begin
        rst_n=0;
        #100 rst_n=1;
    end
    always_ff @( posedge clk ) begin
        if(!rst_n) angle='0;
        else if(en) angle=angle+step;
    end
    cordic #()
    theCordicInst(clk,rst_n,en,angle,cosine,sine);
    //test 45°
    /*
    initial begin
        clk=0;
        rst_n=1;
        en=1;
        repeat(62)
        begin
            #5 clk = ~clk;
            if(cosine || sine)
            begin
                $display((real'(cosine))*(2.0**(-31.0)));
                $display((real'(sine))*(2.0**(-31.0)));
                $display($sin((45.0/180.0)*3.1415926535)); 
            end
        end
    end
    Cordic #()
    theCordicInst(clk,rst_n,en,32'h20000000,cosine,sine);
    */
endmodule

module cordic #(
    parameter integer DW = 32,
    parameter real K = 0.6
)(
    input wire clk,rst_n,en,
    input wire signed [DW-1:0] angle,
    output logic signed [DW-1:0] cosine,sine
);
    wire signed [DW-1:0] atan_table[0:29];
    logic signed [DW-1:0] x_start,y_start;
    logic signed [DW-1:0] x[0:30],y[0:30],z[0:30];
    wire [1:0] domain;

    assign atan_table[00]=32'b00100000000000000000000000000000;
    assign atan_table[01]=32'b00010010111001000000010100011101;
    assign atan_table[02]=32'b00001001111110110011100001011011;
    assign atan_table[03]=32'b00000101000100010001000111010100;
    assign atan_table[04]=32'b00000010100010110000110101000011;
    assign atan_table[05]=32'b00000001010001011101011111100001;
    assign atan_table[06]=32'b00000000101000101111011000011110;
    assign atan_table[07]=32'b00000000010100010111110001010101;
    assign atan_table[08]=32'b00000000001010001011111001010011;
    assign atan_table[09]=32'b00000000000101000101111100101110;
    assign atan_table[10]=32'b00000000000010100010111110011000;
    assign atan_table[11]=32'b00000000000001010001011111001100;
    assign atan_table[12]=32'b00000000000000101000101111100110;
    assign atan_table[13]=32'b00000000000000010100010111110011;
    assign atan_table[14]=32'b00000000000000001010001011111001;
    assign atan_table[15]=32'b00000000000000000101000101111100;
    assign atan_table[16]=32'b00000000000000000010100010111110;
    assign atan_table[17]=32'b00000000000000000001010001011111;
    assign atan_table[18]=32'b00000000000000000000101000101111;
    assign atan_table[19]=32'b00000000000000000000010100010111;
    assign atan_table[20]=32'b00000000000000000000001010001011;
    assign atan_table[21]=32'b00000000000000000000000101000101;
    assign atan_table[22]=32'b00000000000000000000000010100010;
    assign atan_table[23]=32'b00000000000000000000000001010001;
    assign atan_table[24]=32'b00000000000000000000000000101000;
    assign atan_table[25]=32'b00000000000000000000000000010100;
    assign atan_table[26]=32'b00000000000000000000000000001010;
    assign atan_table[27]=32'b00000000000000000000000000000101;
    assign atan_table[28]=32'b00000000000000000000000000000010;
    assign atan_table[29]=32'b00000000000000000000000000000001;

    assign x_start= integer'(K*(2.0**(DW-1.0)));
    assign y_start='0;
    assign domain=angle[31:30];

    // convert angle to -pi/2 ~ pi/2, xy start point changes too
    always_ff @( posedge clk ) begin
        if(~rst_n)
        begin
            x[0] <= '0;
            y[0] <= '0;
            z[0] <= '0;
        end
        else if(en)
        begin
            case(domain)
                2'b00,
                2'b11:
                begin
                    x[0] <=  x_start;
                    y[0] <=  y_start;
                    z[0] <= angle;
                end
                2'b01:
                begin
                    x[0] <= -y_start;
                    y[0] <=  x_start;
                    z[0] <= {2'b00,angle[29:0]};
                end
                2'b10:
                begin
                    x[0] <=  y_start;
                    y[0] <= -x_start;
                    z[0] <= {2'b11,angle[29:0]};
                end
            endcase
        end
    end
    generate
        for(genvar i=0;i<30;i=i+1)
        begin:xyz
            wire s=z[i][31];
            wire signed [DW-1:0] x_shr=x[i]>>>i;
            wire signed [DW-1:0] y_shr=y[i]>>>i;

            always_ff @( posedge clk ) begin
                if(~rst_n)
                begin
                    x[i+1] <= '0;
                    y[i+1] <= '0;
                    z[i+1] <= '0;
                end
                else if(en)
                begin
                    x[i+1] <= s ? x[i]+y_shr : x[i]-y_shr;
                    y[i+1] <= s ? y[i]-x_shr : y[i]+x_shr;
                    z[i+1] <= s ? z[i]+atan_table[i] : z[i]-atan_table[i];
                end
            end
        end
    endgenerate
    assign cosine=x[30];
    assign sine  =y[30];
endmodule

在此处输入图片说明

哔叽

Non-blocking assignments in verilog simulation always assign data in the current clock cycle. The only difference is that it assigns data after all blocking assignments are done.

In your case you have two statements:

always_ff @( posedge clk ) begin
     angle=angle+step;
end

and

always_ff @( posedge clk ) begin
     z[0] <= angle;

At 'posedge clk' two events happen in you case:

  1. blocking assignment updates angle immediately
  2. non-blocking assighment schedules update to z[0] and uses the value of angle updated above.
  3. assigns value to z[0].

It is guaranteed by verilog that 'blocking' happens first. As a result, your z[0] will follow angle.

If you change first assignment to non-blocking

always_ff @( posedge clk ) begin
     angle<=angle+step;
end

the following will happen:

  1. non-blocking assignment schedules update to angle after all blocking assignments. Its value is not changed immediately.
  2. non-blocking assignment schedules update to z[0] but it will use the old value of angle which is not updated yet.
  3. Update values of angle and z[0] using their old values evaluated in the blocking assignment region.

Now, z[0] contains the old value of angle and angle is updated to the new value. This simulates behavior of chained flops and this is the reason for using nbas in all state devices.

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章