高等数字集成电路课程作业(一)

1 向量前导1检测器

1.1 设计功能与要求

设计一个组合逻辑电路,检测输入32位0/1向量中从高到低第一个1出现的位置,如果向量为全0则输出32。例如:

  • 输入00011000 10000000 00000000 00000000,输出3;
  • 输入00000000 11111111 00000000 00000000,输出8;
  • 输入00000000 00000000 00000000 00001010,输出28。

模块输入输出功能定义:

名称 方向 位宽 描述
data_in I 32 输入0/1向量
pos_out O 6 前导1出现位置,取值范围0~32

设计要求:

Verilog实现代码可综合,逻辑延迟越小越好,给出仿真结果。

1.2 算法原理与算法设计

使用二分法计算,从高到低检测:

  1. 先检测输入32位向量的高16位是否有1,若有就继续对该16位进行二分法,若没有就对低16位进行二分法;
  2. 对第1步选择的16位检测其高8位是否有1,若有就继续对该8位进行二分法,若没有就对低8位进行二分法;
  3. 对第2步选择的8位检测其高4位是否有1,若有就继续对该4位进行二分法,若没有就对低4位进行二分法;
  4. 对第3步选择的4位检测其高2位是否有1,若没有就继续该2位进行二分法,若没有就对低2位进行二分法;
  5. 对第4步选择的2位检测其高1位是否为1,若没有就检测其低1为是否为1。

1.3 RTL实现架构

该设计为纯组合逻辑:

  • 输入为 32-bit data_in,输出为 6-bit pos_out

    module seq_head_detect(
        input  wire [31:0] data_in,
        output wire [5:0]  pos_out
    );
    
  • 对于检测输入是否有1,采用或门,one_check为1代表有1,反之则为0:

    assign one_check[4] = |data_in[31:16];
    assign one_check[3] = |data_1[15:8];
    assign one_check[2] = |data_2[7:4];
    assign one_check[1] = |data_3[3:2];
    assign one_check[0] = |data_4[1];
    
  • 对于二分法,根据one_check的值选择继续进行二分法的值:

    assign data_1 = (one_check[4]) ? data_in[31:16] : data_in[15:0];
    assign data_2 = (one_check[3]) ? data_1[15:8] : data_1[7:0];
    assign data_3 = (one_check[2]) ? data_2[7:4] : data_2[3:0];
    assign data_4 = (one_check[1]) ? data_3[3:2] : data_3[1:0];
    
  • 最后,检测所有位数是否有1,有则 pos_out = {1'b0, ~one_check};若没有则 pos_out = 6d'32

    assign pos_out = (|data_in) ? {1'b0, ~one_check} : 6'd32;
    

    data_in 中有1时,用 pos_out 的低5位即可表示,最高位置0。

    假设 data_in 首位为1,则 one_check = 11111,从而 pos_out = 000000;假设 data_in 末位为0,则 one_check = 00000,从而 pos_out = 011111;以此类推 one_check 需要取反。

1.4 RTL仿真结果

1.4.1 测试用例说明

一共有4个测试向量,每个时钟周期换一个测试向量,具体测试向量如下所示:

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        index <= 2'd0;
        data_in_reg <= 32'd0;
    end else begin
        case(index)
            2'd0: data_in_reg <= 32'b0001_1000_1000_0000_0000_0000_0000_0000;
            2'd1: data_in_reg <= 32'b0000_0000_1111_1111_0000_0000_0000_0000;
            2'd2: data_in_reg <= 32'b0000_0000_0000_0000_0000_0000_0000_1010;
            2'd3: data_in_reg <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
            default: data_in_reg <= 32'b0000_0000_0000_0000_0000_0000_0000_0000;
        endcase

        index <= index + 1;
        pos_out_reg <= pos_out;
    end
end

1.4.2 测试结果波形

1DKA6N`2JYL{G8H@_J3~177

  • data_in_reg = 32'b0001_1000_1000_0000_0000_0000_0000_0000 时,pos_out = 3
  • data_in_reg = 32'b0000_0000_1111_1111_0000_0000_0000_0000 时,pos_out = 8
  • data_in_reg = 32'b0000_0000_0000_0000_0000_0000_0000_1010 时,pos_out = 28
  • data_in_reg = 32'b0000_0000_0000_0000_0000_0000_0000_0000 时,pos_out = 32

结论:该输出正确,证明设计正确。

1.5 ASIC综合结果

使用 Design Compiler 进行综合,使用 smic180 工艺,由于该设计为纯组合逻辑设计,不对时钟进行约束,tcl中部分约束如下:

# 使用 slow 工艺角
set target_library " slow.db "
set link_library "* $target_library "
set symbol_library " smic18.sdb "

# 输入驱动使用 NAND2x1
set_driving_cell -lib_cell NAND2X1 data_in

# 输入输出延迟设为 4ns
set_input_delay 4 [get_ports data_in]
set_output_delay 4 [get_ports pos_out]

# 综合后的面积越小越好
set_max_area 0

1.5.1 最大运行频率

由于没有时钟,无法查看最大运行频率。

1.5.2 ASIC面积

DC 综合出来的 ASIC 面积如下:

image-20241102213127417

由于 DC 综合并没有真实走线,主要看总的 cell area,可以看到 Total cell area\(911.433614\mu m^2\),所有单元均为组合逻辑单元,数量为 64,设计中没有时序逻辑单元,数量为 0。

1.5.3 关键路径延时

由于没有时钟约束,因此也无法查看关键路径延时,Path is uncontrained:

image-20241103095351943

1.5.4 综合后的 Schematic

image-20241102215038518

2. 序列检测器

2.1 设计功能与要求

设计一个序列检测同步时序逻辑电路,要实现的功能如下:

当已有输入码流出现序列111000或101110时输出检测信号为1,否则输出为0。在时序上检测到完整序列的下一个时钟周期输出检测结果。输入信号有效为1时表示当前输入有效,否则表示无效。之前输入依旧计入序列中并不清零,即允许序列重叠检测。例如:

输入码流(设输入数据均有效)和输出检测为

[I] 0 0 1 1 1 0 0 0 1 1 0 1 1 1 0 0 0 0

[O] 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1

模块输入输出功能定义:

名称 方向 位宽 描述
clk I 1 系统时钟
rst_n I 1 异步复位,低电平有效
din_vld I 1 输入数据有效指示
din I 1 输入数据
result O 1 输出检测结果

设计要求:

Verilog实现代码可综合,面积越小越好,给出仿真结果。

2.2 算法原理与算法设计

使用mealy状态机,状态转移图如下所示:

a2d7091f60060b254a62f2f0b8e6cc7

2.3 RTL实现架构

使用两个三段式状态机,一个检测序列 111000,另一个检测序列 101110

  • 状态声明:

    reg result_111000, result_101110;
    
    reg [2:0] cstate_111000, cstate_101110;
    reg [2:0] nstate_111000, nstate_101110;
    
    localparam IDLE = 3'b000;
    localparam A    = 3'b001;
    localparam B    = 3'b010;
    localparam C    = 3'b011;
    localparam D    = 3'b100;
    localparam E    = 3'b101;
    
  • 现态转移:

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            cstate_111000 <= IDLE;
            cstate_101110 <= IDLE;
        end else begin
            cstate_111000 <= nstate_111000;
            cstate_101110 <= nstate_101110;
        end
    end
    
  • 次态转移:

    always @(*) begin
        if(din_vld) begin
            nstate_111000 = IDLE;
            nstate_101110 = IDLE;
    
            case(cstate_111000)
                IDLE: nstate_111000 = din ? A : IDLE;
                A   : nstate_111000 = din ? B : IDLE;
                B   : nstate_111000 = din ? C : IDLE;
                C   : nstate_111000 = din ? C : D;
                D   : nstate_111000 = din ? A : E;
                E   : nstate_111000 = din ? A : IDLE;
            endcase
    
            case(cstate_101110)
                IDLE: nstate_101110 = din ? A : IDLE;
                A   : nstate_101110 = din ? A : B;
                B   : nstate_101110 = din ? C : IDLE;
                C   : nstate_101110 = din ? D : B;
                D   : nstate_101110 = din ? E : B;
                E   : nstate_101110 = din ? A : B; 
            endcase
        end else begin
            nstate_111000 = nstate_111000;
            nstate_101110 = nstate_101110;
        end
    end
    
  • 结果输出:

    always @(posedge clk or negedge rst_n) begin
        if(!rst_n) begin
            result_111000 <= 1'b0;
            result_101110 <= 1'b0;
        end else if(din_vld) begin
            case(cstate_111000)
                E: begin
                    if(din == 1'b0)
                        result_111000 <= 1'b1;
                    else
                        result_111000 <= 1'b0;
                end
                default:result_111000 <= 1'b0;
            endcase
    
            case(cstate_101110)
                E: begin
                    if(din == 1'b0)
                        result_101110 <= 1'b1;
                    else
                        result_101110 <= 1'b0;
                end
                default:result_101110 <= 1'b0;
            endcase
        end else begin
            result_111000 <= 1'b0;
            result_101110 <= 1'b0;
        end
    end
    

    最后,模块的result 输出由 result_111000result_101110 或操作得出:

    assign result = result_111000 | result_101110;
    

2.4 仿真结果

2.4.1 测试用例说明

测试输入流如下所示,din 的输入流为 01011100011010110000

为了检测 din_vld 的作用,定义其输入流为 10111111111110111111,其中有两个时钟周期 din_vld = 0,此时 din 的输入无效。

always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        index <= 5'd0;
        din_stream[0:19]     <= 20'b0101_1100_0110_1011_0000;
        din_vld_stream[0:19] <= 20'b1011_1111_1111_1011_1111;
    end else begin
        if(index == 5'd19)
            index <= 5'd0;
        else
            index <= index + 1;

        din <= din_stream[index];
        din_vld <= din_vld_stream[index];
    end
end

2.4.2 测试结果波形

318f41ddb90378293782177ff31e486

如上图所示,

  • 当输入流出现 111000 序列后,result 信号在下一个周期拉高一个时钟周期

  • 当输入流出现 101110 序列后,result 信号在下一个周期拉高一个时钟周期(中间 din_vld 信号拉低了一个周期,此时 din = 0 被忽略)

  • 当输入流出现 111000 序列后,result 信号在下一个周期拉高一个时钟周期(中间 din_vld 信号拉低了一个周期,此时 din = 0 被忽略)

2.5 ASIC综合结果

使用 Design Compiler 进行综合,使用 smic180 工艺,tcl中部分约束如下:

# 使用 slow 工艺角
set target_library " slow.db "
set link_library "* $target_library "
set symbol_library " smic18.sdb "

# 时钟约束
create_clock -period 10 -waveform {0 5} [get_ports clk] -name clk
set_clock_latency 0.1 clk
set_clock_transition 0.2 clk
set_clock_uncertainty 1 -setup clk
set_clock_uncertainty 0.3 -hold clk

# 输入驱动使用 NAND2x1
set_driving_cell -lib_cell NAND2X1 data_in

# 输入输出延迟设为 2ns
set_input_delay 2 -clock [get_clocks clk] {din_vld din}
set_output_delay 2 [get_ports result]

# 综合后的面积越小越好
set_max_area 0

2.5.1 关键路径延时 & 最大运行频率

image-20241103151002367

DC综合后的 timing report 如上所示,

  1. data required time8.88 ns,是满足时序要求的时钟周期时间,也是关键路径延时

  2. 最大运行频率计算公式如下所示,

\[最大运行频率=\frac{1}{关键路径延时} \]

  1. 因此最大运行频率为 \(f_{max} = \frac{1}{8.88 \times 10^{-9}} \approx 112.61MHz\) (时钟约束周期为10ns)

  2. 由于时序裕量(Slack)为 5.20 ns,说明设计目前满足时序要求,并且时钟周期可以进一步优化以提高频率。

2.5.2 ASIC面积

DC 综合出来的 ASIC 面积如下:

image-20241103151928341
  • 由于 DC 综合并没有真实走线,主要看总的 cell area,可以看到 Total cell area\(1127.649616\mu m^2\)

  • Number of cells: 总的逻辑单元数,包括组合逻辑单元和时序单元,共 39 个;

  • Number of combinational cells: 组合逻辑单元数量,共 25 个;

  • Number of sequential cells: 时序逻辑单元数量,共 14 个,这些单元包含触发器或寄存器。

2.5.3 综合后的 Schematic

image-20241103152422826

3. 二进制转BCD码逻辑

3.1 设计功能与要求

设计一个8位无符号二进制数(取值范围0 ~ 255)到10位BCD码的转换组合逻辑电路。其中12位BCD码定义如下:

数据位 描述
9:8 百位BCD码,取值0~2
7:4 十位BCD码,取值0~9
3:0 个位BCD码,取值0~9

例如:

输入 8'b10100101(十进制165),输出 10'b01_0110_0101

输入 8'b11110000(十进制240),输出 10'b10_0100_0000.

模块输入输出功能定义:

名称 方向 位宽 描述
bin_in I 8 输入二进制数
bcd_out O 10 输出BCD编码

设计要求:

Verilog实现代码可综合,逻辑延迟越小越好,给出仿真结果。

3.2 算法原理与算法设计

3.2.1 加三移位法

BCD码范围在 0000~1001 之间,是满十进位的,它只能表示十进制数 0~9。因此要将二进制码转换成BCD码,就要在每4位二进制数的高3位大于等于101时,将其变为10000。

对于每4位二进制数abcd,在最低位输入前,如果高3位的 abc ≥ 0101 时, 对其加上3,最低位d输入,使得加过3的高3位整体左移一位。

这相当于 \((abc + 0011)*2 + d\),即 \(abc*2 + 6 + d\)\(abc*2 + 6 \ge 16\),超过了4位2进制数表示的范围,向更高位进一位,那么此时表示十位的BCD码为0001。

3.2.2 算法步骤

初始化一个移位寄存器 shift_reg,大小为18位,将输入二进制数放在最低的8位,高位用0填充。

重复8次(因为输入是8位):

  • 如果百位BCD码 ≥ 5,则加3调整。
  • 如果十位BCD码 ≥ 5,则加3调整。
  • 如果个位BCD码 ≥ 5,则加3调整。
  • 整个寄存器左移一位。

提取寄存器中的BCD码部分作为输出。

3.3 RTL实现架构

内部信号:

  • shift_reg: 18 位寄存器,用于处理二进制数。它同时存储 BCD 输出(位 [17:8])和输入的二进制数(位 [7:0])。

行为逻辑:

  • 初始化: shift_reg 被初始化为包含输入的二进制数,左侧填充零。
  • 移位加 3 算法:
    • for 循环迭代 8 次,表示输入二进制数的每一位。
    • 每次迭代中:
      • 检查百位、十位和个位 BCD 数字。如果其中任何一位大于等于 5,就将其加 3,以调整为有效的 BCD 编码。
      • shift_reg 左移一位,为下一个二进制位腾出空间。

输出赋值:

  • 转换完成后,shift_reg 的高 10 位(即 shift_reg[17:8])被赋值给 bcd_out,形成输入二进制数的最终 BCD 表示。
module bin2bcd(
    input  wire [7:0] bin_in,
    output wire [9:0] bcd_out 
);
    integer i;
    reg [17:0] shift_reg;

    assign bcd_out[9:0] = shift_reg[17:8];

    always @(*) begin
        shift_reg = {10'd0, bin_in[7:0]};

        for(i = 0; i < 8; i = i+1) begin
            if(shift_reg[17:16] >= 5)
                shift_reg[17:16] = shift_reg[17:16] + 3;

            if(shift_reg[15:12] >= 5)
                shift_reg[15:12] = shift_reg[15:12] + 3;

            if(shift_reg[11:8] >= 5)
                shift_reg[11:8] = shift_reg[11:8] + 3;

            shift_reg = shift_reg << 1;
        end
    end

endmodule

3.4 RTL仿真结果

3.4.1 测试用例说明

initial begin
    bin_in = 8'b0000_0000;
    #10
    bin_in = 8'b1010_0101; //十进制165
    #10
    bin_in = 8'b1111_0000; //十进制240
    #10
    bin_in = 8'b1111_1111; //十进制255
    #10
    $finish; 
end

3.4.2 测试结果波形

image-20241103173015504

  • bin_in 输入为 0000_0000bcd_out 为 0;
  • bin_in 输入为 1010_0101bcd_out 为 165;
  • bin_in 输入为 1111_0000bcd_out 为 240;
  • bin_in 输入为 1111_1111bcd_out 为 255。

可见该设计正确。

3.5 ASIC综合结果

使用 Design Compiler 进行综合,使用 smic180 工艺,tcl中部分约束如下:

set target_library " slow.db "
set link_library "* $target_library "
set symbol_library " smic18.sdb "

set_driving_cell -lib_cell NAND2X1 bin_in
set_input_delay 2 [get_ports bin_in]
set_output_delay 2 [get_ports bcd_out]
set_load 2 [all_outputs]

set_max_area 0 

3.5.1 关键路径延时 & 最大运行频率

由于没有时钟,无法查看最大运行频率。

由于没有时钟约束,因此也无法查看关键路径延时,Path is uncontrained:

image-20241103231858777

3.5.2 ASIC面积

DC 综合出来的 ASIC 面积如下:

image-20241103232207069
  • 由于 DC 综合并没有真实走线,主要看总的 cell area,可以看到 Total cell area\(991.267217 \mu m^2\)

  • Number of cells: 总的逻辑单元数,包括组合逻辑单元和时序单元,共 60 个;

  • Number of combinational cells: 组合逻辑单元数量,共 60 个,表示设计中没有时序逻辑单元,所有逻辑单元均为组合逻辑;

  • Number of sequential cells: 时序逻辑单元数量为 0,说明设计中没有触发器或寄存器。

3.5.3 综合后的Schematic

image-20241103232913463

posted @ 2024-11-10 22:59  Astron_fjh  阅读(8)  评论(0编辑  收藏  举报