中國科學院大學數字集成電路作業開源——算數邏輯章節


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

1、基礎概念問題

1.1請簡要描述10進制整數與16進制整數之間相互轉換方法?

方法1:將10進制整數通過除2取余數的方法得到2進制表示,再將2進制數按照每4位表示一個16進制數的方式轉成16進制表示。

方法2:將10進制整數通過除16取余數的方法直接得到16進制表示。

方法1例子:30轉2進制0001_1110,對應16進制1E

方法2例子:30=30/16=1余下14,14對應的是E。所以30=1E

1.2 請簡要描述數的原碼、反碼和補碼的表示方式和對應表示數值范圍?

原碼:原碼就是符號位加上真值的絕對值, 即用第一位表示符號, 其余位表示值

[+1]原 = 0000 0001

[-1]原 = 1000 0001

8位二進制表示數的范圍:[-127 , 127]

反碼:正數的反碼是其本身。負數的反碼是在其原碼的基礎上, 符號位不變,其余各個位取反

[+1] = [00000001]原 = [00000001]反

[-1] = [10000001]原 = [11111110]反

8位二進制表示數的范圍:[-127 , 127]

補碼:正數的補碼就是其本身。負數的補碼是在其原碼的基礎上, 符號位不變, 其余各位取反, 最后+1. (即在反碼的基礎上+1)

[+1] = [00000001]原 = [00000001]反 = [00000001]補

[-1] = [10000001]原 = [11111110]反 = [11111111]補

8位二進制表示數的范圍:[-128 , 127]

1.3 請簡要描述格雷碼編碼方式和優點,並具體給出5位格雷碼映射關系表格?

格雷碼:在一組數的編碼中,使得任意兩個相鄰的代碼只有一位二進制數不同。二進制轉變為格雷碼通過相鄰位異或實現

優點:在相鄰位間轉換時,只有一位產生變化。大大地減少了由一個狀態到下一個狀態時邏輯的混淆。

自然二進制數 5位格雷碼
00000 00000
00001 00001
00010 00011
00011 00010
00100 00110
00101 00111
00110 00101
00111 00100
01000 01100
01001 01101
01010 01111
01011 01110
01100 01010
01101 01011
01110 01001
01111 01000
10000 11000
10001 11001
10010 11011
10011 11010
10100 11110
10101 11111
10110 11101
10111 11100
11000 10100
11001 10101
11010 10111
11011 10110
11100 10010
11101 10011
11110 10001
11111 10000

1.4 請簡要描述IEEE 754標准中32位單精度浮點數格式、范圍和精度?簡要比較並分析浮點數和定點數在硬件實現方面的優缺點。

img

浮點數:優點:能夠表示高精度,范圍大

​ 缺點:計算電路非常復雜

定點數:優點:計算電路很簡單

​ 缺點:不能夠表示高精度,范圍小

2 基於VerilogHDL進行邏輯電路設計

2.1跑表

設計一個跑表時序邏輯電路,通過按鈕控制及數字顯示,有時分秒顯示,可以清零、開始和暫停。系統主時鍾頻率為10 MHz。

其中按鈕Clear實現清零功能(任意狀態按下時分秒值清零並停止計時)、按鈕Start/Stop實現開始和暫停功能(若當前狀態為停止則按下繼續進行計時,若當前狀態為計時則按下暫停計時)。

數字顯示為XX : XX : XX形式,時分秒各為2位數字。對每位數字使用4位二進制編碼輸出表示(hr_h[3:0],hr_l[3:0] : min_h[3:0],min_l[3:0] : sec_h[3:0],sec_l[3:0])。

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

名稱 方向 位寬 描述
clk I 1 系統時鍾,10 MHz
rst_n I 1 異步復位,低電平有效
clear I 1 清零按鈕,上升沿有效
start_stop I 1 開始/暫停按鈕,上升沿有效
hr_h O 4 時高位輸出,取值0~9
hr_l O 4 時低位輸出,取值0~9
min_h O 4 分高位輸出,取值0~9
min_l O 4 分低位輸出,取值0~9
sec_h O 4 秒高位輸出,取值0~9
sec_l O 4 秒低位輸出,取值0~9

設計要求:

Verilog實現代碼可綜合,給出綜合以及仿真結果(說明:為加快仿真速度,代碼用於仿真時可以縮短秒計數周期,如每100個時鍾周期更新一次秒計數值,正常情況下每10000000個時鍾周期才更新一次秒計數值)。

設計思路:

用counter子模塊實現計數,counter子模塊的功能包括:reset復位,clear清零,計數滿后自動清零並輸出full脈沖。

最低一級的sec_l用clk驅動,用sec_l得到full信號去驅動sec_h,用sec_h的full信號去驅動min_l……一直這樣向上計數。

頂層stop_watch調用counter子模塊連接實現計數功能。用兩段式狀態機實現STOP和START的狀態切換功能,以及產生門控時鍾信號,用來控制計數器工作或停止。

考慮到實際使用的合理性,在計數時,秒鍾和分鍾最大計數值只有59,小時可以最大計數到99(因為題目只說了跑表,沒說是時鍾,所以我就沒有限制不超過24h)

代碼實現:

counter子模塊

module counter (
    input clk,
    input rst_n,
    input clear,
    input [3:0] thresh,
    output full,
    output [3:0] cnt_v
);

    reg [3:0] cnt;
    reg f;

    always @(posedge clk or posedge clear or negedge rst_n) begin
        if(rst_n == 1'b0) begin
            cnt <= 4'b0;
            f <= 1'b0;
        end
        else begin
            if(clear == 1'b1) begin
                cnt <= 4'b0;
                f <= 1'b0;
            end
            else if(cnt_v == thresh) begin
                cnt <= 4'b0;
                f <= 1'b1;
            end
            else begin
                cnt <= cnt + 1'b1;
                f <= 1'b0;
            end
        end
    end

    assign cnt_v = cnt;
    assign full = f;

endmodule

counter的testbench

module testbench();

reg clk;
reg rst_n;
reg [3:0] thresh;
wire full;
wire [3:0] cnt_v;

initial begin
    clk <= 1'b0;
    rst_n <= 1'b0;
    thresh <= 4'b0000;
    #10
    rst_n <= 1'b1;
    thresh <= 4'b1001;
end

always #10 clk <= ~clk;

counter u_cnt(.clk(clk),.rst_n(rst_n),.thresh(thresh),.full(full),.cnt_v(cnt_v));

endmodule

stop_watch頂層

module stop_watch (
    input clk,
    input rst_n,
    input clear,
    input start_stop,
    output [3:0] hr_h,
    output [3:0] hr_l,
    output [3:0] min_h,
    output [3:0] min_l,
    output [3:0] sec_h,
    output [3:0] sec_l
);

    reg clk_en;
    wire clk_gated;

    parameter thresh_h = 4'b0101;
    parameter thresh_l = 4'b1001;
    
    wire full_sec_l;
    wire full_sec_h;
    wire full_min_l;
    wire full_min_h;
    wire full_hr_l;

    parameter START = 1'b0;
    parameter STOP = 1'b1;

    reg current_state;
    reg next_state;

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

    always @(*) begin
        case(current_state)
            START: begin
                if (clear|start_stop) begin
                    next_state = STOP;
                    clk_en = 1'b0;
                end
            end
            STOP: begin
                if (start_stop) begin
                    next_state = START;
                    clk_en = 1'b1;
                end
            end
            default: next_state = START;
        endcase
    end

    assign clk_gated = clk_en & clk;

    counter u_cnt_0(.clk(clk_gated),.rst_n(rst_n),.clear(clear),.thresh(thresh_l),.full(full_sec_l),.cnt_v(sec_l));
    counter u_cnt_1(.clk(full_sec_l),.rst_n(rst_n),.clear(clear),.thresh(thresh_h),.full(full_sec_h),.cnt_v(sec_h));
    counter u_cnt_2(.clk(full_sec_h),.rst_n(rst_n),.clear(clear),.thresh(thresh_l),.full(full_min_l),.cnt_v(min_l));
    counter u_cnt_3(.clk(full_min_l),.rst_n(rst_n),.clear(clear),.thresh(thresh_h),.full(full_min_h),.cnt_v(min_h));
    counter u_cnt_4(.clk(full_min_h),.rst_n(rst_n),.clear(clear),.thresh(thresh_h),.full(full_hr_l),.cnt_v(hr_l));
    counter u_cnt_5(.clk(full_hr_l),.rst_n(rst_n),.clear(clear),.thresh(thresh_h),.full(),.cnt_v(hr_h));
    
endmodule

stop_watch的testbench

module testbench();

reg clk;
reg rst_n;
reg clear;
reg start_stop;
wire [3:0] hr_l,hr_h,min_h,min_l,sec_h,sec_l;

initial begin
    clk <= 1'b0;
    rst_n <= 1'b0;
    clear <= 1'b0;
    start_stop <= 1'b0;
    #1
    rst_n <= 1'b1;
    #10
    clear <= 1'b1;
    #1
    clear <= 1'b0;
    #10
    start_stop <= 1'b1;
    #1
    start_stop <= 1'b0;
    #10
    start_stop <= 1'b1;
    #1
    start_stop <= 1'b0;
    #10
    start_stop <= 1'b1;
    #1
    start_stop <= 1'b0;
end

always #1 clk <= ~clk;

stop_watch u_stop_watch(
    .clk(clk),
    .rst_n(rst_n),
    .clear(clear),
    .start_stop(start_stop),
    .hr_h(hr_h),
    .hr_l(hr_l),
    .min_h(min_h),
    .min_l(min_l),
    .sec_h(sec_h),
    .sec_l(sec_l)
);

endmodule

仿真結果:

counter:計數功能,計數滿后自動清零並產生full脈沖

img

stop_watch:clear實現清零功能(任意狀態按下時分秒值清零並停止計時)。

img

stop_watch:start/stop實現開始和暫停功能(若當前狀態為停止則按下繼續進行計時,若當前狀態為計時則按下暫停計時)。

img

stop_watch:計數功能體現(00:00:00—00:02:04)

img

2.2快速加法器

實現快速加法器組合邏輯,要實現的功能如下:

輸入為兩個16位有符號數,輸出17位相加結果。要求采用超前進位(Carry-look-ahead)結構。

計算例子:

0110000010000000 + 1000000000000001 = 11110000010000001

(24704) + (-32767) = (-8063)

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

名稱 方向 位寬 描述
a I 16 輸入數據,二進制補碼
b I 16 輸入數據,二進制補碼
sum O 17 輸出和a + b,二進制補碼

設計要求:

Verilog實現代碼可綜合,邏輯延遲越小越好,給出綜合以及仿真結果(參考ASIC綜合結果:SMIC 55nm工藝下工作時鍾頻率大於500 MHz)。

設計思路:

設計圖紙如下圖所示(紅色箭頭為進位傳播方向)

img

設計子模塊Carry4實現4位的進位邏輯,使用5個Carry4子模塊按照圖紙所示方式進行連接完成16位超前進位加法器。

對於最高位的處理,進行深入一點的思考。如果兩個16位的運算沒有發生溢出,那么第17位應該是符號位拓展。但如果發生了溢出的話,第17就就得作為符號位了。符號位的確定方式應該是根據最高進位來決定。所以先進行溢出檢測,最高位由溢出信號來進行控制。

最后測試時我用了一組非溢出的數,和正數溢出、負數溢出各一組的數。

代碼實現:

Carry4子模塊

module Carry4(
    input [3:0] p,
    input [3:0] g,
    input cin,
    output P,G,
    output [2:0] cout
);

    assign P = &p;
    assign G = g[3]|(p[3]&g[2])|(p[3]&p[2]&g[1])|(p[3]&p[2]&p[1]&g[0]);

    assign cout[0] = g[0]|(p[0]&cin);
    assign cout[1] = g[1]|(p[1]&g[0])|(p[1]&p[0]&cin);
    assign cout[2] = g[2]|(p[2]&g[1])|(p[2]&p[1]&g[0])|(p[2]&p[1]&p[0]&cin);

endmodule

add_tc_16_16頂層

module add_tc_16_16 (
    input [15:0] a,
    input [15:0] b,
    output [16:0] sum
);
    
    wire [15:0] p = a|b;
    wire [15:0] g = a&b;
    wire [3:0] P,G;
    wire [15:0] c;
    wire [15:0] out;
    wire overflow;
    wire sign;

    assign c[0] = 1'b0;

    Carry4 U_Carry4_0(.p(p[3:0]),.g(g[3:0]),.cin(c[0]),.P(P[0]),.G(G[0]),.cout(c[3:1]));
    Carry4 U_Carry4_1(.p(p[7:4]),.g(g[7:4]),.cin(c[4]),.P(P[1]),.G(G[1]),.cout(c[7:5]));
    Carry4 U_Carry4_2(.p(p[11:8]),.g(g[11:8]),.cin(c[8]),.P(P[2]),.G(G[2]),.cout(c[11:9]));
    Carry4 U_Carry4_3(.p(p[15:12]),.g(g[15:12]),.cin(c[12]),.P(P[3]),.G(G[3]),.cout(c[15:13]));
    Carry4 U_Carry4_4(.p(P),.g(G),.cin(c[0]),.P(),.G(),.cout({c[12],c[8],c[4]}));

    assign cout = (a[15] & b[15]) | (a[15] & c[15]) | (b[15] & c[15]);
    assign out = (~a & ~b & c) | (~a & b & ~c) | (a & ~b & ~c) | (a & b & c);

    assign overflow = (out[15] & ~a[15] & ~b[15]) | (~out[15] & a[15] & b[15]);

    assign sign = (overflow & cout) | (~overflow & out[15]);

    assign sum = {sign,out};

endmodule

testbench:

module testbench();

reg [15:0] a;
reg [15:0] b;
wire [16:0] sum;

initial begin
  a <= 16'b0110_0000_1000_0000;
  b <= 16'b1000_0000_0000_0001;
  #10
  a <= 16'b1000_0000_0000_0001;
  b <= 16'b1000_0000_0000_0001;
  #10
  a <= 16'b0111_1111_1111_1111;
  b <= 16'b0111_1111_1111_1111;
end

add_tc_16_16 U_Add16(
    .a(a),
    .b(b),
    .sum(sum)
);

endmodule

仿真結果:

三組測試輸入

0110000010000000 + 1000000000000001 = 11110000010000001

1000000000000001 + 1000000000000001 = 10000000000000010

0111111111111111 + 0111111111111111 = 01111111111111110

第一組數沒有溢出,最高位是符號位拓展,后兩者發生了溢出,最高位根據進位來決定

img

2.3快速乘法器

實現快速乘法器組合邏輯,要實現的功能如下:

輸入為兩個16位有符號數,輸出32位相乘結果。要求采用Booth編碼和Wallace樹型結構。

計算例子:

0110000010000000 * 1000000000000001 = 11001111110000000110000010000000

(24704) * (-32767) = (-809475968)

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

名稱 方向 位寬 描述
a I 16 輸入數據,二進制補碼
b I 16 輸入數據,二進制補碼
product O 32 輸出乘積a * b,二進制補碼

設計要求:

Verilog實現代碼可綜合,邏輯延遲越小越好,給出綜合以及仿真結果(參考ASIC綜合結果:SMIC 55nm工藝下工作時鍾頻率大於500 MHz)。

設計思路:

本題目要求實現16位快速乘法器,我采用了Radix-4的booth編碼用於產生部分積以及wallence tree的加法器結構

設計參考自下圖:

img

先通過Radix-4 Booth編碼電路將輸入B進行編碼(booth_enc),再與A一起產生8個部分積(gen_prod),在booth子模塊中進行它們的連接。這8個部分積通過Wallence Tree結構的加法樹移位求和后(6個CSA (adder)),最后通過RSA(adder)求和得到乘算結果,在wallence tree子模塊中進行他們的連接。頂層文件mul_tc_16_16連接booth和wallence_tree。

img

img

參考資料:https://zhuanlan.zhihu.com/p/143802580

代碼實現:

booth_enc booth編碼

module booth_enc (
    input [2:0] code,
    output neg,
    output zero,
    output one,
    output two
);

    assign neg = code[2];
    assign zero = (code == 3'b000) || (code == 3'b111);
    assign two = (code == 3'b100) || (code == 3'b011);
    assign one = !zero & !two;

endmodule

gen_prod 用於控制部分積的產生

module gen_prod (
    input [15:0] a,
    input neg,
    input zero,
    input one,
    input two,
    output [31:0] prod
);

    reg [31:0] prod_pre;

    always @(*) begin
        prod_pre = 32'b0;
        if(zero) prod_pre = 32'b0;
        else if(one) prod_pre = {{16{a[15]}},a};
        else if(two) prod_pre = {{15{a[15]}},a,1'b0};
    end

    assign prod = neg ? (~prod_pre + 1'b1) : prod_pre;

endmodule

booth 完整booth編碼模塊

module booth (
    input [15:0] a,
    input [15:0] b,
    output [31:0] prod_0,
    output [31:0] prod_1,
    output [31:0] prod_2,
    output [31:0] prod_3,
    output [31:0] prod_4,
    output [31:0] prod_5,
    output [31:0] prod_6,
    output [31:0] prod_7
);
    

    wire [7:0] neg;
    wire [7:0] zero;
    wire [7:0] one;
    wire [7:0] two;

    booth_enc u_booth_enc_0(.code({b[1:0],1'b0}),.neg(neg[0]),.zero(zero[0]),.one(one[0]),.two(two[0]));
    booth_enc u_booth_enc_1(.code(b[3:1]),.neg(neg[1]),.zero(zero[1]),.one(one[1]),.two(two[1]));
    booth_enc u_booth_enc_2(.code(b[5:3]),.neg(neg[2]),.zero(zero[2]),.one(one[2]),.two(two[2]));
    booth_enc u_booth_enc_3(.code(b[7:5]),.neg(neg[3]),.zero(zero[3]),.one(one[3]),.two(two[3]));
    booth_enc u_booth_enc_4(.code(b[9:7]),.neg(neg[4]),.zero(zero[4]),.one(one[4]),.two(two[4]));
    booth_enc u_booth_enc_5(.code(b[11:9]),.neg(neg[5]),.zero(zero[5]),.one(one[5]),.two(two[5]));
    booth_enc u_booth_enc_6(.code(b[13:11]),.neg(neg[6]),.zero(zero[6]),.one(one[6]),.two(two[6]));
    booth_enc u_booth_enc_7(.code(b[15:13]),.neg(neg[7]),.zero(zero[7]),.one(one[7]),.two(two[7]));

    gen_prod gen_prod_0(.a(a),.neg(neg[0]),.zero(zero[0]),.one(one[0]),.two(two[0]),.prod(prod_0));
    gen_prod gen_prod_1(.a(a),.neg(neg[1]),.zero(zero[1]),.one(one[1]),.two(two[1]),.prod(prod_1));
    gen_prod gen_prod_2(.a(a),.neg(neg[2]),.zero(zero[2]),.one(one[2]),.two(two[2]),.prod(prod_2));
    gen_prod gen_prod_3(.a(a),.neg(neg[3]),.zero(zero[3]),.one(one[3]),.two(two[3]),.prod(prod_3));
    gen_prod gen_prod_4(.a(a),.neg(neg[4]),.zero(zero[4]),.one(one[4]),.two(two[4]),.prod(prod_4));
    gen_prod gen_prod_5(.a(a),.neg(neg[5]),.zero(zero[5]),.one(one[5]),.two(two[5]),.prod(prod_5));
    gen_prod gen_prod_6(.a(a),.neg(neg[6]),.zero(zero[6]),.one(one[6]),.two(two[6]),.prod(prod_6));
    gen_prod gen_prod_7(.a(a),.neg(neg[7]),.zero(zero[7]),.one(one[7]),.two(two[7]),.prod(prod_7));
    
endmodule

adder 加法器,可以聲明位寬

module adder#(parameter DATA_WIDTH = 1) (
    input [DATA_WIDTH-1:0] a,
    input [DATA_WIDTH-1:0] b,
    input [DATA_WIDTH-1:0] cin,
    output [DATA_WIDTH-1:0] cout,
    output [DATA_WIDTH-1:0] s
);
    generate
        genvar index;
        for (index = 0; index < DATA_WIDTH ; index = index + 1'b1) begin
            assign {cout[index],s[index]} = a[index] + b[index] + cin[index];
        end
    endgenerate
endmodule

wallence_tree 用加法器連接成wallence tree

module wallence_tree (
    input [31:0] prod_0,
    input [31:0] prod_1,
    input [31:0] prod_2,
    input [31:0] prod_3,
    input [31:0] prod_4,
    input [31:0] prod_5,
    input [31:0] prod_6,
    input [31:0] prod_7,
    output [31:0] product
);
    
    wire [31:0] csa_0_c;
    wire [31:0] csa_0_s;
    wire [31:0] csa_1_c;
    wire [31:0] csa_1_s;    
    wire [31:0] csa_2_c;
    wire [31:0] csa_2_s;
    wire [31:0] csa_3_c;
    wire [31:0] csa_3_s;
    wire [31:0] csa_4_c;
    wire [31:0] csa_4_s;
    wire [31:0] csa_5_c;
    wire [31:0] csa_5_s;

    adder #(.DATA_WIDTH(32)) u_csa_0(.a(prod_0),.b(prod_1<<2),.cin(prod_2<<4),.cout(csa_0_c),.s(csa_0_s));
    adder #(.DATA_WIDTH(32)) u_csa_1(.a(prod_3<<6),.b(prod_4<<8),.cin(prod_5<<10),.cout(csa_1_c),.s(csa_1_s));
    adder #(.DATA_WIDTH(32)) u_csa_2(.a(csa_1_c),.b(prod_6<<12),.cin(prod_7<<14),.cout(csa_2_c),.s(csa_2_s));
    adder #(.DATA_WIDTH(32)) u_csa_3(.a(csa_0_c),.b(csa_0_s),.cin(csa_1_s),.cout(csa_3_c),.s(csa_3_s));
    adder #(.DATA_WIDTH(32)) u_csa_4(.a(csa_2_s),.b(csa_3_s),.cin(csa_3_c),.cout(csa_4_c),.s(csa_4_s));
    adder #(.DATA_WIDTH(32)) u_csa_5(.a(csa_2_c),.b(csa_4_s),.cin(csa_4_c),.cout(csa_5_c),.s(csa_5_s));

    adder #(.DATA_WIDTH(32)) u_rsa(.a(csa_5_s),.b(csa_5_c << 1),.cin(32'b0),.cout(),.s(product));

endmodule

mul_tc_16_16 頂層模塊

module mul_tc_16_16 (
    input [15:0] a,
    input [15:0] b,
    output [31:0] product
);

    wire [31:0] prod_0;
    wire [31:0] prod_1;
    wire [31:0] prod_2;
    wire [31:0] prod_3;
    wire [31:0] prod_4;
    wire [31:0] prod_5;
    wire [31:0] prod_6;
    wire [31:0] prod_7;
    
    booth u_booth(
        .a(a),
        .b(b),
        .prod_0(prod_0),
        .prod_1(prod_1),
        .prod_2(prod_2),
        .prod_3(prod_3),
        .prod_4(prod_4),
        .prod_5(prod_5),
        .prod_6(prod_6),
        .prod_7(prod_7)
    );

    wallence_tree u_wallence_tree(
        .prod_0(prod_0),
        .prod_1(prod_1),
        .prod_2(prod_2),
        .prod_3(prod_3),
        .prod_4(prod_4),
        .prod_5(prod_5),
        .prod_6(prod_6),
        .prod_7(prod_7),
        .product(product)
    );

endmodule

testbench

module testbench ();

reg [15:0] a;
reg [15:0] b;
wire [31:0] product;

initial begin
    a <= 16'b0110_0000_1000_0000;
    b <= 16'b1000_0000_0000_0001;
end

mul_tc_16_16 u_uml_tc_16_16(
    .a(a),
    .b(b),
    .product(product)
);

endmodule

仿真結果:

0110000010000000 * 1000000000000001 = 11001111110000000110000010000000

(24704) * (-32767) = (-809475968)

部分積(prod0—prod7)與手算的結果也是一致的

img

2.4桶形移位器

實現桶形移位器組合邏輯,要實現的功能如下:

輸入為32位二進制向量,根據方向和位移值輸出循環移位后的32位結果。例如:

輸入向量00011000101000000000000000000000,方向左,位移值10,輸出向量10000000000000000000000001100010

輸入向量00000000111111110000000000000011,方向右,位移植20,輸出向量11110000000000000011000000001111.

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

名稱 方向 位寬 描述
data_in I 32 輸入數據
dir I 1 位移方向 0:循環左移 1:循環右移
sh I 5 位移值,取值0~31
data_out O 32 輸出數據

設計要求:

Verilog實現代碼可綜合,邏輯延遲越小越好,給出綜合以及仿真結果。

設計思路:

設計參考自下圖:

img

在上圖的基礎上增加的功能主要有:移動方向的控制;移位碼共有五位,因此共有五層的移位邏輯;循環移位。

代碼實現:

module bsh_32 (
    input [31:0] data_in,
    input dir,
    input [4:0] sh,
    output [31:0] data_out
);

    reg [31:0] out;

    always @(*) begin
        case(dir)
            1'b0: begin
                out = sh[0] ? {data_in[30:0], data_in[31]} : data_in;
                out = sh[1] ? {out[29:0], out[31:30]} : out;
                out = sh[2] ? {out[27:0], out[31:28]} : out;
                out = sh[3] ? {out[23:0], out[31:24]} : out;
                out = sh[4] ? {out[15:0], out[31:16]} : out;
            end
            1'b1:begin
                out = sh[0] ? {data_in[0], data_in[31:1]} : data_in;
                out = sh[1] ? {out[1:0], out[31:2]} : out;
                out = sh[2] ? {out[3:0], out[31:4]} : out;
                out = sh[3] ? {out[7:0], out[31:8]} : out;
                out = sh[4] ? {out[15:0], out[31:16]} : out;
            end
        endcase
    end

    assign data_out = out;

endmodule

testbench

module testbench();

reg [31:0] data_in;
reg dir;
reg [4:0] sh;
wire [31:0] data_out;

initial begin
    data_in <= 32'b0001_1000_1010_0000_0000_0000_0000_0000;
    dir <= 1'b0;
    sh <= 5'd10;
    #10
    data_in <= 32'b0000_0000_1111_1111_0000_0000_0000_0011;
    dir <= 1'b1;
    sh <= 5'd20;
end

bsh_32 u_bsh_32(
    .data_in(data_in),
    .dir(dir),
    .sh(sh),
    .data_out(data_out)
);

endmodule

仿真結果:

輸入向量00011000101000000000000000000000,方向左,位移值10,輸出向量10000000000000000000000001100010;

輸入向量00000000111111110000000000000011,方向右,位移值20,輸出向量11110000000000000011000000001111.

img


免責聲明!

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



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