中國科學院大學數字集成電路作業開源——時序邏輯與存儲器章節


中國科學院大學數字集成電路作業開源——第6-8章

1、基礎概念問題

1.1 請簡要描述Mealy狀態機與Moore狀態機的特性及它們之間的聯系?

Moore狀態機:輸出僅僅與當前狀態有關;

Mealy狀態機:輸出不僅取決於當前狀態,還和輸入有關;

Mealy和Moore機之間可以相互轉化,對於每個Mealy機,都有一個等價的Moore機,Moore機狀態的上限為所對應的Mealy機狀態的數量和輸出數量的乘積。

1.2 請簡要描述SRAM的特點以及實際應用中與寄存器堆邏輯的優缺點比較?

SRAM的特點是工作速度快,只要電源不撤除,寫入SRAM的信息就不會消失,不需要刷新電路,同時在讀出時不破壞原來存放的信息,一經寫入可多次讀出,但集成度較低,功耗較大。

寄存器堆邏輯實現上更為簡單,但性能遠不如工藝優化后的SRAM。而SRAM性能雖強,但是實現上略微復雜。

1.3 請簡要描述門控時鍾常見電路結構以及適用范圍?

門控時鍾電路結構包括直接將控制信號與時鍾信號進行與操作(會產生毛刺,因此實際中很少使用)

img

基於鎖存器的時鍾門控方案(常用於ASIC設計)

img

基於觸發器的時鍾門控方案(常用於FPGA設計)

img

1.4 請簡要描述AHB總線與APB總線的基本特性以及在實際SoC系統中它們的適用場合?

AHB(Advanced High-performance Bus), 為高速總線,一般用來連接高速外設。APB (Advanced Peripheral Bus) 為低速總線,一般用來接低速外設。

在SoC系統中,AHB總線會掛載ARM處理器,RAM,DMA控制器等設備,通過AHB2APB Bridge與APB總線連接,APB上掛在UART,Timer等低速設備。

img

2 基於Verilog HDL進行邏輯電路設計

2.1平方根計算

設計一個時序邏輯電路,計算32位非負整數的平方根。對於輸入x,計算y = floor(sqrt(x)),即y是平方后不超過x的最大非負整數。例如:

l 輸入x = 256,輸出y = 16

l 輸入x = 255,輸出y = 15

l 輸入x = 2147483648,輸出y = 46340

l 輸入x = 4294967295,輸出y = 65535

頂層模塊名為sqrt_u32,輸入輸出功能定義:

名稱 方向 位寬 描述
clk I 1 系統時鍾
rst_n I 1 系統異步復位,低電平有效
vld_in I 1 輸入數據有效指示
x I 32 輸入被開方數據
vld_out O 1 輸出數據有效指示
y O 16 輸出結果數據

設計要求:

l Verilog實現代碼可綜合,給出綜合以及仿真結果。

l 能夠處理多組數據輸入,且從數據輸入到結果輸出延遲周期數盡量少。

設計思路:

一開始想到的思路是cordic算法或者牛頓迭代法,測試后發現的問題是:對於32位數的輸入,cordic算法會存在不能收斂的問題(c語言測試,16次迭代,0-4的輸入可以精確的開方,更大的數可以通過歸一化然后處理,一直到256都能正確工作,但測試到32位的時候發現數據小的部分已經算不對了,如果非要用cordic的話就得寫很復雜的小數位數轉換邏輯了,所以我最后還是放棄了)牛頓迭代法中要用到除法,但我不想調用除法器ip,最后采用了逐次逼近法(其實可以理解成逆向的牛頓迭代)

逐次逼近算法流程如圖 所示,首先數據輸入data[7:0],接着設置實驗值D_z[3:0]和確定值D_q[3:0],然后按照從高往低的順序,依次將每一位置1(如D_z[3]置1),再將實驗值平方后與輸入數據比較,若實驗值的平方大於輸入值(D_z^2 > data),則此位為0(D_q[3]為0),反之(D_z^2 ≤ data)此位為1(D_q[3]為1);以此迭代到最后一位。

可見,如果是n bit的數據,那么需要n/2次迭代,每次計算如果一個周期,則需要n/2個周期。

借鑒:https://blog.csdn.net/qq_39507748/article/details/115468883

img

代碼實現:

module sqrt_u32 (
    input clk,
    input rst_n,
    input vld_in,
    input [31:0] x,
    output reg vld_out,
    output reg [16:0] y
);
    parameter d_width = 32;
    parameter q_width = 16;

    reg     [d_width-1:0] D     [q_width:1]; 
    reg     [q_width-1:0] Q_z   [q_width:1]; 
    reg     [q_width-1:0] Q_q   [q_width:1]; 
    reg     valid_flag          [q_width:1]; 

    always@(posedge clk or negedge  rst_n)
        begin
            if(!rst_n)
                begin
                    D[q_width] <= 0;
                    Q_z[q_width] <= 0;
                    Q_q[q_width] <= 0;
                    valid_flag[q_width] <= 0;
                end
            else if(vld_in)
                begin
                    D[q_width] <= x; 
                    Q_z[q_width] <= {1'b1,{(q_width-1){1'b0}}}; 
                    Q_q[q_width] <= 0; 
                    valid_flag[q_width] <= 1;
                end
            else
                begin
                    D[q_width] <= 0;
                    Q_z[q_width] <= 0;
                    Q_q[q_width] <= 0;
                    valid_flag[q_width] <= 0;
                end
        end

        generate
            genvar i; 
                for(i=q_width-1;i>=1;i=i-1)
                    begin:U
                        always@(posedge clk or negedge  rst_n)
                            begin
                                if(!rst_n)
                                    begin
                                        D[i] <= 0;
                                        Q_z[i] <= 0;
                                        Q_q[i] <= 0;
                                        valid_flag[i] <= 0;
                                    end
                                else    if(valid_flag[i+1])
                                    begin
                                        if(Q_z[i+1]*Q_z[i+1] > D[i+1])
                                            begin
                                                Q_z[i] <= {Q_q[i+1][q_width-1:i],1'b1,{{i-1}{1'b0}}};
                                                Q_q[i] <= Q_q[i+1];
                                            end
                                        else
                                            begin
                                                Q_z[i] <= {Q_z[i+1][q_width-1:i],1'b1,{{i-1}{1'b0}}};
                                                Q_q[i] <= Q_z[i+1];
                                            end
                                        D[i] <= D[i+1];
                                        valid_flag[i] <= 1;
                                    end
                                else
                                    begin
                                        valid_flag[i] <= 0;
                                        D[i] <= 0;
                                        Q_q[i] <= 0;
                                        Q_z[i] <= 0;
                                    end
                            end
                    end
        endgenerate

        always@(posedge clk or negedge  rst_n) 
            begin
                if(!rst_n)
                    begin
                        y <= 0;
                        vld_out <= 0;
                    end
                else    if(valid_flag[1])
                    begin
                        if(Q_z[1]*Q_z[1] > D[1])
                            begin
                                y <= Q_q[1];
                                vld_out <= 1;
                            end
                        else
                            begin
                                y <= {Q_q[1][q_width-1:1],Q_z[1][0]};
                                vld_out <= 1;
                            end
                    end
                else
                    begin
                        y <= 0;
                        vld_out <= 0;
                    end
            end

endmodule

testbench

module testbench ();
    
    reg clk,rst_n,vld_in;
    reg [31:0] x;
    wire vld_out;
    wire [15:0] y;

    initial begin
        clk <= 1'b0;
        rst_n <= 1'b0;
        vld_in <= 1'b0;
        x <= 0;
        #30
        rst_n <= 1'b1;
        vld_in <= 1'b1;
        x <= 256;
        #20
        x <= 255;
        #20
        x <= 2147483648;
        #20 
        x <= 4294967295;
    end

    always #10 clk <= ~clk;

    sqrt_u32 u_sqrt_u32(
        .clk(clk),
        .rst_n(rst_n),
        .vld_in(vld_in),
        .x(x),
        .vld_out(vld_out),
        .y(y)
    );

endmodule

仿真結果:

結果距離輸入延遲了16個周期

img

img

輸出值

輸入值img

2.2數據排序

設計一個時序邏輯電路,對輸入32個8位無符號整數從小到大進行排序(若存在多個數據值相等,則不分先后,見例子)。例如:

l 輸入32個數據依次為:31, 29, 27, 25, 23, 21, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1, 2, 2, 4, 4, 4, 4, 8, 16, 8, 16, 32, 32, 0, 10, 20, 30

l 輸出32個數據依次為:0, 1, 2, 2, 3, 4, 4, 4, 4, 5, 7, 8, 8, 9, 10, 11, 13, 15, 16, 16, 17, 19, 20, 21, 23, 25, 27, 29, 30, 31, 32, 32

頂層模塊名為sort_32_u8,輸入輸出功能定義:

名稱 方向 位寬 描述
clk I 1 系統時鍾
rst_n I 1 系統異步復位,低電平有效
vld_in I 1 輸入數據有效指示
din_0, din_1, … …, din_31 I 8 輸入數據0,輸入數據1,……, 輸入數據31
vld_out O 1 輸出數據有效指示
dout_0, dout_1, … …, dout_31 O 8 輸出數據0,輸出數據1,……, 輸出數據31

注:din_0 ~ din_31共32個輸入端口,dout_0~ dout_31共32個輸出端口。輸出數據dout_0 ~ dout_31的值從小到大排列。

設計要求:

l Verilog實現代碼可綜合,給出綜合以及仿真結果。

l 邏輯資源和延遲需要做權衡,使得數據輸入到結果輸出延遲周期數盡量少。

設計思路:

原先的思路是使用雙調排序,但雙調排序只能排單調增和單調減的,題目里面的有重復數的情況調不了。

采用冒泡排序,導致排序時間非常長,消耗資源比較少,不符合題目要求的延遲盡可能小。本來想再寫個全比較排序,但別的作業太多了,姑且算了,其實邏輯也挺簡單的。

冒泡實際上就是兩層循環反復比較,循環變量換成計數器就行了,使用了狀態機進行控制。

參考:http://www.manongjc.com/detail/19-ezdqhndvpyqmqjx.html

代碼實現:

module sort_32_u8 (
    input clk,
    input rst_n,
    input vld_in,
    input [7:0] din_0,din_1,din_2,din_3,din_4,din_5,din_6,din_7,din_8,din_9,din_10,din_11,din_12,din_13,din_14,din_15,din_16,din_17,din_18,din_19,din_20,din_21,din_22,din_23,din_24,din_25,din_26,din_27,din_28,din_29,din_30,din_31,    
    output reg vld_out,
    output [7:0] dout_0,dout_1,dout_2,dout_3,dout_4,dout_5,dout_6,dout_7,dout_8,dout_9,dout_10,dout_11,dout_12,dout_13,dout_14,dout_15,dout_16,dout_17,dout_18,dout_19,dout_20,dout_21,dout_22,dout_23,dout_24,dout_25,dout_26,dout_27,dout_28,dout_29,dout_30,dout_31
);

    parameter s_rst = 2'b00;
    parameter s_load = 2'b01;
    parameter s_sort = 2'b10;
    parameter s_out = 2'b11;

    reg [4:0] cnt_i,turn;
    reg [7:0] data_fifo [31:0];
    reg [1:0] cur_state,next_state;
    reg reset,load_data,swap;

    always @(posedge clk or negedge rst_n) begin
        if(rst_n == 1'b0) begin
            cur_state <= s_rst;
        end
        else begin
            cur_state <= next_state;
        end
    end

    always @(posedge clk) begin
        if(reset == 1'b1) begin
            data_fifo[0]  <= 0;
            data_fifo[1]  <= 0;
            data_fifo[2]  <= 0;
            data_fifo[3]  <= 0;
            data_fifo[4]  <= 0;
            data_fifo[5]  <= 0;
            data_fifo[6]  <= 0;
            data_fifo[7]  <= 0;
            data_fifo[8]  <= 0;
            data_fifo[9]  <= 0;
            data_fifo[10] <= 0;
            data_fifo[11] <= 0;
            data_fifo[12] <= 0;
            data_fifo[13] <= 0;
            data_fifo[14] <= 0;
            data_fifo[15] <= 0;
            data_fifo[16] <= 0;
            data_fifo[17] <= 0;
            data_fifo[18] <= 0;
            data_fifo[19] <= 0;
            data_fifo[20] <= 0;
            data_fifo[21] <= 0;
            data_fifo[22] <= 0;
            data_fifo[23] <= 0;
            data_fifo[24] <= 0;
            data_fifo[25] <= 0;
            data_fifo[26] <= 0;
            data_fifo[27] <= 0;
            data_fifo[28] <= 0;
            data_fifo[29] <= 0;
            data_fifo[30] <= 0;
            data_fifo[31] <= 0;
            turn <= 0;
            cnt_i <= 0;
        end
        else if(load_data == 1'b1) begin
            data_fifo[0]  <=  din_0;
            data_fifo[1]  <=  din_1;
            data_fifo[2]  <=  din_2;
            data_fifo[3]  <=  din_3;
            data_fifo[4]  <=  din_4;
            data_fifo[5]  <=  din_5;
            data_fifo[6]  <=  din_6;
            data_fifo[7]  <=  din_7;
            data_fifo[8]  <=  din_8;
            data_fifo[9]  <=  din_9;
            data_fifo[10] <= din_10;
            data_fifo[11] <= din_11;
            data_fifo[12] <= din_12;
            data_fifo[13] <= din_13;
            data_fifo[14] <= din_14;
            data_fifo[15] <= din_15;
            data_fifo[16] <= din_16;
            data_fifo[17] <= din_17;
            data_fifo[18] <= din_18;
            data_fifo[19] <= din_19;
            data_fifo[20] <= din_20;
            data_fifo[21] <= din_21;
            data_fifo[22] <= din_22;
            data_fifo[23] <= din_23;
            data_fifo[24] <= din_24;
            data_fifo[25] <= din_25;
            data_fifo[26] <= din_26;
            data_fifo[27] <= din_27;
            data_fifo[28] <= din_28;
            data_fifo[29] <= din_29;
            data_fifo[30] <= din_30;
            data_fifo[31] <= din_31;

            turn <= 31;
            cnt_i <= 0;
        end
        else if(swap == 1'b1) begin
            if(cnt_i < turn) begin
                cnt_i <= cnt_i + 1;
                if(data_fifo[cnt_i+1] < data_fifo[cnt_i]) begin
                    data_fifo[cnt_i+1] <= data_fifo[cnt_i];
                    data_fifo[cnt_i] <= data_fifo[cnt_i+1];
                end
            end
            else begin
                cnt_i <= 1;
                turn <= turn - 1;
                if(data_fifo[1] < data_fifo[0]) begin
                    data_fifo[1] <= data_fifo[0];
                    data_fifo[0] <= data_fifo[1];
                end
            end
        end
    end

    always @(cnt_i,cur_state,turn,vld_in) begin
        next_state <= s_rst;
        case (cur_state)
            s_rst : begin
                reset <= 1'b1;
                vld_out <= 1'b0;
                next_state <= s_load;
            end 
            s_load : begin
                reset <= 1'b0;
                if(vld_in == 1'b1) begin
                    load_data <= 1'b1;
                    vld_out <= 1'b0;
                    next_state <= s_sort;
                end
                else begin
                    next_state <= s_load;
                end
            end
            s_sort : begin
                swap <= 1'b1;
                load_data <= 1'b0;
                if(turn == 1 && cnt_i ==1 ) begin
                    next_state <= s_out;
                    vld_out <= 1'b1;
                end
                else begin
                    next_state <= s_sort;
                end
            end
            s_out : begin
                next_state <= s_load;
                swap <= 1'b0;
            end
            default : begin
                next_state <= s_rst;
            end
        endcase
    end

    assign dout_0  =  data_fifo[0];
    assign dout_1  =  data_fifo[1];
    assign dout_2  =  data_fifo[2];
    assign dout_3  =  data_fifo[3];
    assign dout_4  =  data_fifo[4];
    assign dout_5  =  data_fifo[5];
    assign dout_6  =  data_fifo[6];
    assign dout_7  =  data_fifo[7];
    assign dout_8  =  data_fifo[8];
    assign dout_9  =  data_fifo[9];
    assign dout_10 = data_fifo[10];
    assign dout_11 = data_fifo[11];
    assign dout_12 = data_fifo[12];
    assign dout_13 = data_fifo[13];
    assign dout_14 = data_fifo[14];
    assign dout_15 = data_fifo[15];
    assign dout_16 = data_fifo[16];
    assign dout_17 = data_fifo[17];
    assign dout_18 = data_fifo[18];
    assign dout_19 = data_fifo[19];
    assign dout_20 = data_fifo[20];
    assign dout_21 = data_fifo[21];
    assign dout_22 = data_fifo[22];
    assign dout_23 = data_fifo[23];
    assign dout_24 = data_fifo[24];
    assign dout_25 = data_fifo[25];
    assign dout_26 = data_fifo[26];
    assign dout_27 = data_fifo[27];
    assign dout_28 = data_fifo[28];
    assign dout_29 = data_fifo[29];
    assign dout_30 = data_fifo[30];
    assign dout_31 = data_fifo[31];
    
endmodule

testbench

module testbench ();
    
    reg clk,rst_n,vld_in;
    reg [7:0] din_0,din_1,din_2,din_3,din_4,din_5,din_6,din_7,din_8,din_9,din_10,din_11,din_12,din_13,din_14,din_15,din_16,din_17,din_18,din_19,din_20,din_21,din_22,din_23,din_24,din_25,din_26,din_27,din_28,din_29,din_30,din_31;    
    wire vld_out;
    wire [7:0] dout_0,dout_1,dout_2,dout_3,dout_4,dout_5,dout_6,dout_7,dout_8,dout_9,dout_10,dout_11,dout_12,dout_13,dout_14,dout_15,dout_16,dout_17,dout_18,dout_19,dout_20,dout_21,dout_22,dout_23,dout_24,dout_25,dout_26,dout_27,dout_28,dout_29,dout_30,dout_31;

    initial begin
        rst_n <= 1'b0;
        clk <= 1'b0;
        vld_in <= 1'b0;
        din_0  <= 31;
        din_1  <= 29;
        din_2  <= 27;
        din_3  <= 25;
        din_4  <= 23;
        din_5  <= 21;
        din_6  <= 19;
        din_7  <= 17;
        din_8  <= 15;
        din_9  <= 13;
        din_10 <= 11;
        din_11 <= 9;
        din_12 <= 7;
        din_13 <= 5;
        din_14 <= 3;
        din_15 <= 1;
        din_16 <= 2;
        din_17 <= 2;
        din_18 <= 4;
        din_19 <= 4; 
        din_20 <= 4; 
        din_21 <= 4; 
        din_22 <= 8;
        din_23 <= 16;
        din_24 <= 8; 
        din_25 <= 16;
        din_26 <= 32;
        din_27 <= 32;
        din_28 <= 0; 
        din_29 <= 10;
        din_30 <= 20;
        din_31 <= 30;
        #30
        rst_n <= 1'b1;
        vld_in <= 1'b1;
        #20
        vld_in <= 1'b0;
    end

    always #10 clk <= ~clk;

    sort_32_u8 u_sort(
        clk,
        rst_n,
        vld_in,
        din_0,din_1,din_2,din_3,din_4,din_5,din_6,din_7,din_8,din_9,din_10,din_11,din_12,din_13,din_14,din_15,din_16,din_17,din_18,din_19,din_20,din_21,din_22,din_23,din_24,din_25,din_26,din_27,din_28,din_29,din_30,din_31,
        vld_out,
        dout_0,dout_1,dout_2,dout_3,dout_4,dout_5,dout_6,dout_7,dout_8,dout_9,dout_10,dout_11,dout_12,dout_13,dout_14,dout_15,dout_16,dout_17,dout_18,dout_19,dout_20,dout_21,dout_22,dout_23,dout_24,dout_25,dout_26,dout_27,dout_28,dout_29,dout_30,dout_31
    );

endmodule

仿真結果:

花了將近500個周期排序(真的太慢了,我選算法的沒注意到要求延遲周期盡可能少)

dout_15到dout_31

img

dout_0到dout_19

img

2.3 矩陣掃描

如圖所示,ZigZag掃描就是將8x8的矩陣塊按照箭頭運動方向重新排列(從1開始到64結束):

img


設計一個時序邏輯電路,對輸入64個整數(按照行優先方式構成8x8的矩陣塊)按照ZigZag掃描方式依次輸出。例如:

l 輸入64個數據依次為:1, 2, 3, 4, ..., 61, 62, 63, 64

l 輸出64個數據依次為:1, 2, 9, 17, 10, 3, ..., 62, 55, 48, 56, 63, 64

頂層模塊名為mat_scan,輸入輸出功能定義:

名稱 方向 位寬 描述
clk I 1 系統時鍾
rst_n I 1 系統異步復位,低電平有效
vld_in I 1 輸入數據有效指示
din I 10 輸入數據
vld_out O 1 輸出數據有效指示
dout O 10 輸出數據

注:每組輸入數據連續64個周期輸入,即vld_in連續64個時鍾周期有效;每組輸出數據連續64個周期輸出,即vld_out連續64個時鍾周期有效。

設計要求:

l Verilog實現代碼可綜合,給出綜合以及仿真結果。

l 使用SRAM緩存輸入數據,SRAM使用bit數盡量少。

l 從數據輸入到結果輸出延遲周期數盡量少。

設計思路:

用sram存64個數,輸入的時候地址順序增加,讀出的時候用狀態機實現zigzag要求的順序對應的地址變化。(我查了一下zigzag自動掃描的c語言實現,感覺在verilog里面實現太困難,所以就這樣暴力解決了)

代碼實現:

module mat_scan (
    input clk,
    input rst_n,
    input vld_in,
    input [9:0] din,
    output reg vld_out,
    output [9:0] dout
);

    reg cs_n, w_en, r_en;
    reg [5:0] addr;

    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
            addr <= 6'b11_1111;
            cs_n <= 1'b1;
            w_en <= 1'b0;
            r_en <= 1'b0;
            vld_out <= 1'b0;
        end
        else if (vld_in) begin
            w_en <= 1'b1;
            cs_n <= 1'b0;
            addr <= addr + 1'b1;
        end
        else begin
            vld_out <= 1'b1;
            r_en <= 1'b1;
            case (addr)
                6'b11_1111 : addr <= 6'b00_0000;
                6'b00_0000 : addr <= 6'b00_0001;
                6'b00_0001 : addr <= 6'b00_1000;
                6'b00_1000 : addr <= 6'b01_0000;
                6'b01_0000 : addr <= 6'b00_1001;
                6'b00_1001 : addr <= 6'b00_0010;
                6'b00_0010 : addr <= 6'b00_0011;
                6'b00_0011 : addr <= 6'b00_1010;
                6'b00_1010 : addr <= 6'b01_0001;
                6'b01_0001 : addr <= 6'b01_1000;
                6'b01_1000 : addr <= 6'b10_0000;
                6'b10_0000 : addr <= 6'b01_1001;
                6'b01_1001 : addr <= 6'b01_0010;
                6'b01_0010 : addr <= 6'b00_1011;
                6'b00_1011 : addr <= 6'b00_0100;
                6'b00_0100 : addr <= 6'b00_0101;
                6'b00_0101 : addr <= 6'b00_1100;
                6'b00_1100 : addr <= 6'b01_0011;
                6'b01_0011 : addr <= 6'b01_1010;
                6'b01_1010 : addr <= 6'b10_0001;
                6'b10_0001 : addr <= 6'b10_1000;
                6'b10_1000 : addr <= 6'b11_0000;
                6'b11_0000 : addr <= 6'b10_1001;
                6'b10_1001 : addr <= 6'b10_0010;
                6'b10_0010 : addr <= 6'b01_1011;
                6'b01_1011 : addr <= 6'b01_0100;
                6'b01_0100 : addr <= 6'b00_1101;
                6'b00_1101 : addr <= 6'b00_0110;
                6'b00_0110 : addr <= 6'b00_0111;
                6'b00_0111 : addr <= 6'b00_1110;
                6'b00_1110 : addr <= 6'b01_0101;
                6'b01_0101 : addr <= 6'b01_1100;
                6'b01_1100 : addr <= 6'b10_0011;
                6'b10_0011 : addr <= 6'b10_1010;
                6'b10_1010 : addr <= 6'b11_0001;
                6'b11_0001 : addr <= 6'b11_1000;
                6'b11_1000 : addr <= 6'b11_1001;
                6'b11_1001 : addr <= 6'b11_0010;
                6'b11_0010 : addr <= 6'b10_1011;
                6'b10_1011 : addr <= 6'b10_0100;
                6'b10_0100 : addr <= 6'b01_1101;
                6'b01_1101 : addr <= 6'b01_0110;
                6'b01_0110 : addr <= 6'b00_1111;
                6'b00_1111 : addr <= 6'b01_0111;
                6'b01_0111 : addr <= 6'b01_1110;
                6'b01_1110 : addr <= 6'b10_0101;
                6'b10_0101 : addr <= 6'b10_1100;
                6'b10_1100 : addr <= 6'b11_0011;
                6'b11_0011 : addr <= 6'b11_1010;
                6'b11_1010 : addr <= 6'b11_1011;
                6'b11_1011 : addr <= 6'b11_0100;
                6'b11_0100 : addr <= 6'b10_1101;
                6'b10_1101 : addr <= 6'b10_0110;
                6'b10_0110 : addr <= 6'b01_1111;
                6'b01_1111 : addr <= 6'b10_0111;
                6'b10_0111 : addr <= 6'b10_1110;
                6'b10_1110 : addr <= 6'b11_0101;
                6'b11_0101 : addr <= 6'b11_1100;
                6'b11_1100 : addr <= 6'b11_1101;
                6'b11_1101 : addr <= 6'b11_0110;
                6'b11_0110 : addr <= 6'b10_1111;
                6'b10_1111 : addr <= 6'b11_0111;
                6'b11_0111 : addr <= 6'b11_1110;
                6'b11_1110 : addr <= 6'b11_1111;
            endcase
        end
    end

    sram #(
        .ADDR_DEPTH(6),
        .DATA_WIDTH(10),
        .DATA_DEPTH(64)
    ) u_sram (
        .clk(clk),
        .rst_n(rst_n),
        .cs_n(cs_n),
        .w_en(w_en),
        .r_en(r_en),
        .addr(addr),
        .din(din),
        .dout(dout)
    );

endmodule

sram

module sram #(
    parameter ADDR_DEPTH = 4,
    parameter DATA_WIDTH = 8,
    parameter DATA_DEPTH = 16
)(
    input clk,
    input rst_n,
    input cs_n,
    input w_en,
    input r_en,
    input [ADDR_DEPTH-1:0] addr,
    input [DATA_WIDTH-1:0] din,
    output reg [DATA_WIDTH-1:0] dout
);
    
    reg [DATA_WIDTH-1:0] mem [DATA_DEPTH-1:0];
    integer i;

    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
            for (i = 0; i < DATA_DEPTH; i=i+1) begin
                mem[i] <= (1'b0 << (DATA_WIDTH-1));
            end
        end
        else if (w_en == 1'b1 && cs_n == 1'b0) begin
            mem[addr] <= din;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
            dout <= (1'b0 << (DATA_WIDTH-1));
        end
        else if (r_en == 1'b1 && cs_n == 1'b0) begin
            dout <= mem[addr];
        end
        else begin
            dout <= dout;
        end
    end

endmodule

testbench

module testbench ();
    reg clk, rst_n, vld_in;
    reg [9:0] din;
    wire vld_out;
    wire [9:0] dout;

    initial begin
        clk <= 1'b0;
        rst_n <= 1'b0;
        din <= 10'b00_0000_0000;
        vld_in <= 1'b0;
        #20
        rst_n <= 1'b1;
        vld_in <= 1'b1;
        #1290
        vld_in <= 1'b0;
    end

    always #10 clk <= ~clk;

    always @(posedge clk) begin
        if (vld_in == 1'b1) begin
           din <= din + 1'b1;
        end
    end

    mat_scan u_mat_scan(
        .clk(clk),
        .rst_n(rst_n),
        .vld_in(vld_in),
        .din(din),
        .vld_out(vld_out),
        .dout(dout)
    );

endmodule

仿真結果:

先順序遞增的將1-64放到SRAM里面去

img

輸出的地址用狀態機實現,從而搞定zigzag掃描

img

img

2.4 AHB-SRAM控制器

設計一個基於AHB從接口的單端口SRAM控制器,實現SRAM存儲器與AHB總線的數據信息交換,將AHB總線上的讀寫操作轉換成標准SRAM讀寫操作。

SRAM大小為4096x32-bit,AHB接口數據大小固定為32-bit,AHB接口地址范圍為0x00000000 – 0x00003FFC。AHB接口能夠實現單次或突發模式的數據讀寫操作。

頂層模塊名為sram_ctr_ahb,輸入輸出功能定義:

名稱 方向 位寬 描述
hclk I 1 系統時鍾
hresetn I 1 系統異步復位,低電平有效
hwrite I 1 寫有效
htrans I 2 當前傳輸類型
hsize I 3 當前傳輸大小
haddr I 32 讀寫地址
hburst I 3 當前突發類型
hwdata I 32 寫數據
hready O 1 傳輸完成指示
hresp O 2 傳輸響應
hrdata O 32 讀數據
sram_csn O 1 SRAM片選,低電平有效
sram_wen O 1 SRAM寫使能,低電平有效
sram_a O 12 SRAM讀寫地址
sram_d O 32 SRAM寫數據
sram_q I 32 SRAM讀數據

注:仿真時SRAM時鍾與hclk相同,SRAM可以用FPGA的單端口SRAM IP核仿真模型或者使用單端口SRAM行為級模型代替。

設計要求:

設計要求:

l Verilog實現代碼可綜合,給出綜合以及仿真結果。

仿真時應給出各種典型情況下的數據讀寫接口信號波形。

設計思路:

作為實際設計並跑過完整ARM SoC的人,我覺得其實這里的輸入激勵應該由一個ARM CPU或者別的CPU來生成,但是沒有這個條件就只能手寫了。

在我做ARM SoC時,一個經驗之談是給ARM核寫代碼的時候(C語言),經常int_8/uint_8,int_16/uint_16或者int_32/int_32這些聲明了位寬的變量

例如當時寫的外設的寄存器表(基地址+偏移量)

img

所以很現實的就是存儲器的hsize要支持8位,16位和32位(更大的我寫的時候沒用到)

一個我想多數人意識不到的問題是CPU訪存是以字節(Byte)為單位的,即8bit。所以如果存儲器的數據寬度和總線寬度對齊,是32位的話,就必須得考慮訪存地址在haddr上左移的問題。如果直接是32位的訪存,那么haddr就必須左移兩位,16位的訪存就得左移一位,8位不用移位。或者就是直接用8位寬度的存儲器,如果要32位數的話就連續讀4個數出來放到總線上,16位數就連續讀2個數放到總線上。我用了前者(32位寬的存儲器)

代碼實現:

module sram_ctr_ahb (
    input hclk,
    input hresetn,
    input hwrite,
    input [1:0] htrans,
    input [2:0] hsize,
    input [31:0] haddr,
    input [2:0] hburst,
    input [31:0] hwdata,
    output reg hready,
    output reg [1:0] hresp,
    output reg [31:0] hrdata,
    output reg sram_csn,
    output reg sram_wen,
    output reg [11:0] sram_a,
    output reg [31:0] sram_d,
    input [31:0] sram_q
);

    reg [31:0] hwdata_mask;

    always @(posedge hclk or negedge hresetn) begin
        if(hresetn == 1'b0) begin
            hwdata_mask <= 0;
            hready <= 1'b1;
            hresp <= 2'b0;
            hrdata <= 0;
            sram_csn <= 1'b0;
            sram_wen <= 1'b0;
            sram_a <= 0;
            sram_d <= 0;
        end
        else begin
            sram_wen <= hwrite & htrans[1];
            sram_a <= haddr[13:2];
        end
    end

    always @(posedge hclk or negedge hresetn) begin
         if(hresetn == 1'b0) begin
            hrdata <= 0;
            sram_d <= 0;
        end
        else begin
            case (hsize[1:0])
                2'b10: hwdata_mask <=  32'hFFFFFFFF;                        // Word write
                2'b01: hwdata_mask <= (32'h0000FFFF << (16 * haddr[1]));    // Halfword write
                2'b00: hwdata_mask <= (32'h000000FF << (8 * haddr[1:0]));   // Byte write
                default: hwdata_mask <= 32'hFFFFFFFF;            
            endcase
            sram_d <= (hwdata & hwdata_mask) | (hrdata & ~hwdata_mask);
            hrdata <= sram_q;
        end
    end

endmodule

sram

module sram #(
    parameter ADDR_DEPTH = 4,
    parameter DATA_WIDTH = 8,
    parameter DATA_DEPTH = 16
)(
    input clk,
    input rst_n,
    input cs_n,
    input w_en,
    input [ADDR_DEPTH-1:0] addr,
    input [DATA_WIDTH-1:0] din,
    output reg [DATA_WIDTH-1:0] dout
);
    
    reg [DATA_WIDTH-1:0] mem [DATA_DEPTH-1:0];
    integer i;

    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
            for (i = 0; i < DATA_DEPTH; i=i+1) begin
                mem[i] <= (1'b0 << (DATA_WIDTH-1));
            end
        end
        else if (w_en == 1'b1 && cs_n == 1'b0) begin
            mem[addr] <= din;
        end
    end

    always @(posedge clk or negedge rst_n) begin
        if (rst_n == 1'b0) begin
            dout <= (1'b0 << (DATA_WIDTH-1));
        end
        else if (cs_n == 1'b0) begin
            dout <= mem[addr];
        end
    end

endmodule

testbench

module testbench ();
    
    reg hclk;
    reg hresetn;
    reg hwrite;
    reg [1:0] htrans;
    reg [2:0] hsize;
    reg [31:0] haddr;
    reg [2:0] hburst;
    reg [31:0] hwdata;
    wire hready;
    wire [1:0] hresp;
    wire [31:0] hrdata;
    wire sram_csn;
    wire sram_wen;
    wire [11:0] sram_a;
    wire [31:0] sram_d;
    wire [31:0] sram_q;

    initial begin
        hclk <= 1'b0;
        hresetn <= 1'b0;
        hwrite <= 1'b0;
        htrans <= 2'b00;
        hsize <= 3'b000;
        haddr <= 32'h00000000;
        hburst <= 3'b000;
        hwdata <= 32'h00000000;
        #20
        hresetn <= 1'b1;
        hwrite <= 1'b1;
        htrans <= 2'b10;
        hsize <= 3'b010;
        #20
        haddr <= 32'hfffffffc;
        hwdata <= 32'h20211212;
        #20
        hsize <= 3'b001;
        haddr <= 32'hfffffffE;
        hwdata <= 32'habcd0000;
        #20
        hsize <= 3'b000;
        haddr <= 32'hffffffff;
        hwdata <= 32'h59000000;
        #20
        hwrite <= 1'b0;
    end

    always #10 hclk <= ~hclk;

    sram_ctr_ahb u_sram_ctrl_ahb(
        .hclk(hclk),
        .hresetn(hresetn),
        .hwrite(hwrite),
        .htrans(htrans),
        .hsize(hsize),
        .haddr(haddr),
        .hburst(hburst),
        .hwdata(hwdata),
        .hready(hready),
        .hresp(hresp),
        .hrdata(hrdata),
        .sram_csn(sram_csn),
        .sram_wen(sram_wen),
        .sram_a(sram_a),
        .sram_d(sram_d),
        .sram_q(sram_q)
    );

    sram #(
        .ADDR_DEPTH(12),
        .DATA_WIDTH(32),
        .DATA_DEPTH(4096)
    ) u_sram (
        .clk(hclk),
        .rst_n(hresetn),
        .cs_n(sram_csn),
        .w_en(sram_wen),
        .addr(sram_a),
        .din(sram_d),
        .dout(sram_q)
    );

endmodule

仿真結果:

寫三個數(hwdata)(位寬不同),地址分別是fffffffc,fffffffe,ffffffff

img

單口sram,sram_wen取消后開始讀這三個數(hrdata)

img


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM