高等数字集成电路课程作业(一)
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 算法原理与算法设计
使用二分法计算,从高到低检测:
- 先检测输入32位向量的高16位是否有1,若有就继续对该16位进行二分法,若没有就对低16位进行二分法;
- 对第1步选择的16位检测其高8位是否有1,若有就继续对该8位进行二分法,若没有就对低8位进行二分法;
- 对第2步选择的8位检测其高4位是否有1,若有就继续对该4位进行二分法,若没有就对低4位进行二分法;
- 对第3步选择的4位检测其高2位是否有1,若没有就继续该2位进行二分法,若没有就对低2位进行二分法;
- 对第4步选择的2位检测其高1位是否为1,若没有就检测其低1为是否为1。
1.3 RTL实现架构
该设计为纯组合逻辑:
-
输入为 32-bit
data_in
,输出为 6-bitpos_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 测试结果波形
- 当
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 面积如下:
由于 DC 综合并没有真实走线,主要看总的 cell area
,可以看到 Total cell area
为 \(911.433614\mu m^2\),所有单元均为组合逻辑单元,数量为 64,设计中没有时序逻辑单元,数量为 0。
1.5.3 关键路径延时
由于没有时钟约束,因此也无法查看关键路径延时,Path is uncontrained:
1.5.4 综合后的 Schematic
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状态机,状态转移图如下所示:
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_111000
和result_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 测试结果波形
如上图所示,
-
当输入流出现
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 关键路径延时 & 最大运行频率
DC综合后的 timing report 如上所示,
-
data required time
为8.88 ns
,是满足时序要求的时钟周期时间,也是关键路径延时。 -
最大运行频率计算公式如下所示,
-
因此最大运行频率为 \(f_{max} = \frac{1}{8.88 \times 10^{-9}} \approx 112.61MHz\) (时钟约束周期为10ns)
-
由于时序裕量(Slack)为
5.20 ns
,说明设计目前满足时序要求,并且时钟周期可以进一步优化以提高频率。
2.5.2 ASIC面积
DC 综合出来的 ASIC 面积如下:
-
由于 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
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 测试结果波形
- 当
bin_in
输入为0000_0000
,bcd_out
为 0; - 当
bin_in
输入为1010_0101
,bcd_out
为 165; - 当
bin_in
输入为1111_0000
,bcd_out
为 240; - 当
bin_in
输入为1111_1111
,bcd_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:
3.5.2 ASIC面积
DC 综合出来的 ASIC 面积如下:
-
由于 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,说明设计中没有触发器或寄存器。