当前位置:网站首页>FPGA(八)RTL代码之四(基本电路设计1)
FPGA(八)RTL代码之四(基本电路设计1)
2022-06-29 03:04:00 【简宸~】
系列文章目录
FPGA(四)数字IC面试的四个基本问题
FPGA(五)RTL代码之一(跨时钟域设计)
FPGA(六)RTL代码之二(复杂电路设计1)
FPGA(七)RTL代码之三(复杂电路设计2)
文章目录
前言
这不马上要面试了嘛,有些慌!HDLbits的题目已经刷完了,但又知道自己还远远不够,就从一个B站UP主那里截取了一个刷题的题库,只有题目,代码都要自己去写,所以写的代码可能存在问题,我打算分成好几节把刷的题目代码放出来。仅供自己学习。如果有需要的也可以看看,若有错误,还望指出!谢谢!

一、1位全加器
全加器是能够计算低位进位的二进制加法电路。与半加器相比,全加器不只考虑本位计算结果是否有进位,也考虑上一位对本位的进位,可以把多个一位全加器级联后做成多位全加器。
1. 半加器
1)概念
半加器电路是指对两个输入数据位相加,输出一个结果位和进位,没有进位输入的加法器电路。 是实现两个一位二进制数的加法运算电路。
2)逻辑电路

3) 真值表
| 被加数A | 加数B | 和数S | 进位数C |
|---|---|---|---|
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 0 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 1 |
4) 输出表达式
S = A ‾ B + A B ‾ = A ⊕ B , C = A B 或 { C , S } = A + B S= \overline{A}B+A \overline{B}=A⊕B, C = A B \\ 或 \{C,S\} = A + B S=AB+AB=A⊕B,C=AB或{ C,S}=A+B
5)RTL代码
module add_half(
input a ,//被加数A
input b ,//加数B
output c ,//进位数C
output s //和数S
);
//①分开对和数S和进位数C进行逻辑运算
//assign c = a & b;
//assign s = a ^ b;
//②使用位拼接运算符
assign {
c,s} = a + b;
endmodule
2. 全加器
1) 概念
全加器英语名称为full-adder,是用门电路实现两个二进制数相加并求出和的组合线路,称为一位全加器。一位全加器可以处理低位进位,并输出本位加法进位。多个一位全加器进行级联可以得到多位全加器。
2) 逻辑电路

3) 真值表
| 低位进位数 C i n C_{in} Cin | 被加数A | 加数B | 和数S | 进位数 C o u t C_{out} Cout |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 1 | 0 | 1 |
| 1 | 1 | 0 | 0 | 1 |
| 1 | 1 | 1 | 1 | 1 |
4) 输出表达式
S = A ⊕ B ⊕ C i n , C o u t = A B + C i n ( A + B ) 或 者 第 二 个 表 达 式 也 可 以 用 一 个 异 或 门 : C o u t = A B + C i n ( A ⊕ B ) 或 { C o u t , S } = A + B + C i n S=A⊕B⊕C_{in},C_{out} = AB+C_{in}(A+B) \\ 或者第二个表达式也可以用一个异或门:C_{out}= AB+C_{in}(A⊕B) \\ 或\{C_{out},S\}=A+B+C_{in} S=A⊕B⊕Cin,Cout=AB+Cin(A+B)或者第二个表达式也可以用一个异或门:Cout=AB+Cin(A⊕B)或{ Cout,S}=A+B+Cin
5) RTL代码
module hull_adder(
input a ,
input b ,
input cin ,
output cout,
output sum
);
//①逻辑运算
//sum = a ^ b ^ c;
//cout = (a & b) + (cin & (a ^ b));
//②拼接运算符
assign {
cout,sum} = a + b + cin;
endmodule
附 原理图方式实现:FPGA——1位全加器的实现
二、4位全加器
四位全加器也有不同形式:Verilog——串行四位加法器和超前四位加法器74HC283
我现在用到的基本上都是串行的。
RTL代码
module top_module(
input [3:0] a, b,
input cin,
output [3:0] cout,
output [3:0] sum );
//①实现一个四位全加器,这个行为描述很好懂,但是RTL图比较混乱 这里是不需要用到子模块的
/* always @(*) begin integer i; for(i=0;i<4;i++) {cout[i],sum[i]} = a[i] + b[i] + (i==0?cin:cout[i-1]); end */
//②这个就是有多少位就例化多少次,代码量稍微多一些,但是RTL图很清晰 需要用到子模块
/* genvar i; generate for(i=0;i<3;i++) begin: adder if(i == 0) begin hull_adder u0_hull_adder( .a (a[0]), .b (b[0]), .cin (cin), .cout (cout[0]), .sum (sum[0]) ); end else begin hull_adder ui_hull_adder( .a (a[i]), .b (b[i]), .cin (cout[i-1]), .cout (cout[i]), .sum (sum[i]) ); end end endgenerate */
//③这个就是上一个的改良版,优化了代码
genvar i;
generate
for(i=0;i<3;i++) begin: adder
hull_adder ui_hull_adder(
.a (a[i]),
.b (b[i]),
.cin ((i==0?cin:cout[i-1])),
.cout (cout[i]),
.sum (sum[i])
);
end
endgenerate
endmodule
module hull_adder(
input a ,
input b ,
input cin ,
output cout,
output sum
);
assign {
cout,sum} = a + b + cin;
endmodule
三、4-1多路选择器
1. 概念
假设选择端有 n n n位数据,则可对 2 n 2^n 2n位输入数据进行选择,来决定输出 Y Y Y与哪一位输入数据相关。
2. RTL代码
1) 采用case语句
module data_selector(
input [1:0] sel ,
input [3:0] in ,
output reg out
);
//若括号里均为0,则out必为0,完全可以不执行always语句
always @(sel or in) begin
case({
sel[1],sel[0]})
2'b00: out <= in[0];
2'b01: out <= in[1];
2'b10: out <= in[2];
2'b11: out <= in[3];
default: out <= 1'bx;
endcase
end
endmodule
2) 采用assign语句
module data_selector(
input D0,D1,D2,D3,s0,s1,
output y
);
wire [1:0] SEL;
wire A,B,C,D;
assign SEL = {
s1,s0};
assign A = (SEL == 2'b00);
assign B = (SEL == 2'b01);
assign C = (SEL == 2'b10);
assign D = (SEL == 2'b11);
assign y = (D0 & A)|(D1 & B)|(D2 & C)|(D3 & D);
endmodule
其实还可以采用if条件语句,和case差不多。
四、奇数分频器
参考文章:奇数分频器的实现
我本来想直接用上面这篇文章的,但想到还有占空比的问题,于是还是自己“改”一下吧。这一部分很多的话以及图都是用的上面这篇文章的。
1. 分频器介绍
奇数分频(2N+1),以三分频(N=1)为例,三分频即分频后的时钟半周期(周期)是原时钟半周期(周期)的3倍宽度。
偶数分频我们只需要借助原时钟的上升沿或者下降沿进行计数分频,但是要实现奇数分频我们就要同时利用原时钟的上升沿和下降沿。可以通过原时钟的上升沿和下降沿计数产生clk1和clk2,再通过clk1和clk2的关系实现奇数分频。这种关系可以是或、与、异或,详细如下所述。
在分频的时候,有时候也会有占空比,在比较小的分频情况下,只会有20%、25%、40%等要求,但是一旦分频数比较大的时候,比如十分频这样的(当然会比较少哈),就会有一些奇怪的数。不过万变不离其宗,方法都是一样的。
2. 通过时钟 “相或” 实现奇数分频
原时钟clk通过上升沿和下降沿计数产生时钟clk1和clk2,clk1和clk2 相或 产生clk三分频的clk_div3时钟。
- clk1:原时钟clk的上升沿产生。
- clk2:原时钟clk的下降沿产生。
①3分频
高电平:低电平 = 1:2

//时钟 相或
//只需要修改①处和②处的判断语句,就可以修改占空比
module top (
input clk_150m,
input rst_n,
output clk_div3
);
reg [3:0] cnt_1;//上升沿计数
reg [3:0] cnt_2;//下降沿计数
reg clk_1;
reg clk_2;
assign clk_div3 = clk_1 | clk_2;
[email protected](posedge clk_150m or negedge rst_n) begin
if(!rst_n) begin
cnt_1 <= 4'd0;
clk_1 <= 1'b0;
end
else begin
cnt_1 <= (cnt_1 == 4'd2)?4'd0:cnt_1+4'd1;
clk_1 <= (cnt_1 == 4'd1)?1'b1:1'b0; //① //只修改为cnt1 == 4'd0,就可以变成占空比为66%,但此时就不是三分频了
end
end
[email protected](negedge clk_150m or negedge rst_n)
begin
if(!rst_n) begin
cnt_2 <= 4'd0;
clk_2 <= 1'b0;
end
else begin
cnt_2 <= (cnt_2 == 4'd2)?4'd0:cnt_2+4'd1;
clk_2 <= (cnt_2 == 4'd1)?1'b1:1'b0; //② //只修改为cnt2 == 4'd0,就可以变成占空比为33%,但此时就不是三分频了
end
end
endmodule

②2N+1分频
高电平:低电平 = N:N+1
//时钟 相或
module top #(
parameter NUM = 6)
(
input clk_150m,
input rst_n,
output clk_div3
);
reg [NUM-1:0] cnt_1;//上升沿计数
reg [NUM-1:0] cnt_2;//下降沿计数
reg clk_1;
reg clk_2;
//当NUM的值为奇数时,并且需要50%的占空比时,选择clk1 | clk2;
//当NUM的值为奇数时,并且需要50%的占空比时,只用clk_div3 = clk_1或clk_div3 = clk_2
assign clk_div3 = clk_1 | clk_2;
//assign clk_div3 = clk_1;
[email protected](posedge clk_150m or negedge rst_n) begin
if(!rst_n)
begin
cnt_1 <= 4'd0;
clk_1 <= 1'b0;
end
else begin
cnt_1 <= (cnt_1 == NUM-1)?0:cnt_1+1;
clk_1 <= (cnt_1 <= NUM/2-1)?1'b1:1'b0;
end
end
[email protected](negedge clk_150m or negedge rst_n)
begin
if(!rst_n) begin
cnt_2 <= 4'd0;
clk_2 <= 1'b0;
end
else begin
cnt_2 <= (cnt_2 == NUM-1)?0:cnt_2+1;
clk_2 <= (cnt_2 <= NUM/2-1)?1'b1:1'b0;
end
end
endmodule
3. 通过时钟 “相与” 实现奇数分频
原时钟clk通过上升沿和下降沿计数产生时钟clk1和clk2,clk1和clk2 相与 产生clk三分频的clk_div3时钟。
- clk1:原时钟clk的上升沿产生。
- clk2:原时钟clk的下降沿产生。
①3分频
高电平:低电平 = 2:1

//时钟 相与
//一样的,修改①②两处判断语句的值就可以修改占空比
module top (
input clk_150m,
input rst_n,
output clk_div3
);
reg [3:0] cnt_1;//上升沿计数
reg [3:0] cnt_2;//下降沿计数
reg clk_1;
reg clk_2;
assign clk_div3 = clk_1 & clk_2;
[email protected](posedge clk_150m or negedge rst_n) begin
if(!rst_n) begin
cnt_1 <= 4'd0;
clk_1 <= 1'b0;
end
else begin
cnt_1 <= (cnt_1 == 4'd2)?4'd0:cnt_1+4'd1;
clk_1 <= (cnt_1 <= 4'd1)?1'b1:1'b0; //①
end
end
[email protected](negedge clk_150m or negedge rst_n) begin
if(!rst_n) begin
cnt_2 <= 4'd0;
clk_2 <= 1'b0;
end
else begin
cnt_2 <= (cnt_2 == 4'd2)?4'd0:cnt_2+4'd1;
clk_2 <= (cnt_2 <= 4'd1)?1'b1:1'b0; //②
end
end
endmodule

②2N+1分频
高电平:低电平 = N+1:N
//时钟 相与
module top #(
parameter NUM = 5
)(
input clk_150m,
input rst_n,
output clk_div3
);
reg [NUM-1:0] cnt_1;//上升沿计数
reg [NUM-1:0] cnt_2;//下降沿计数
reg clk_1;
reg clk_2;
//当NUM的值为奇数时,并且需要50%的占空比时,选择clk1 & clk2;
//当NUM的值为奇数时,并且需要50%的占空比时,只用clk_div3 = clk_1或clk_div3 = clk_2
assign clk_div3 = clk_1 & clk_2;
[email protected](posedge clk_150m or negedge rst_n) begin
if(!rst_n) begin
cnt_1 <= 4'd0;
clk_1 <= 1'b0;
end
else begin
cnt_1 <= (cnt_1 == NUM-1)?0:cnt_1+1;
clk_1 <= (cnt_1 <= (NUM-1)/2)?1'b1:1'b0;
end
end
[email protected](negedge clk_150m or negedge rst_n) begin
if(!rst_n) begin
cnt_2 <= 4'd0;
clk_2 <= 1'b0;
end
else begin
cnt_2 <= (cnt_2 == NUM-1)?0:cnt_2+1;
clk_2 <= (cnt_2 <= (NUM-1)/2)?1'b1:1'b0;
end
end
endmodule
4. 通过时钟 “相异或” 实现奇数分频
原时钟clk通过上升沿和下降沿计数产生时钟clk1和clk2,clk1和clk2 相异或 产生clk三分频的clk_div3时钟。
- clk1:原时钟clk的上升沿产生。
- clk2:原时钟clk的下降沿产生。
① 3分频
clk1以原时钟clk的3个周期为单位翻转,clk2在clk1时钟的中间点发生翻转。

//时钟 相异或
//一样的,修改①②两处占空比的值就可以修改占空比
module top (
input clk_150m,
input rst_n,
output clk_div3
);
reg [3:0] cnt_1;//上升沿计数
reg clk_1;
reg clk_2;
assign clk_div3 = clk_1 ^ clk_2;
[email protected](posedge clk_150m or negedge rst_n) begin
if(!rst_n) begin
cnt_1 <= 4'd0;
clk_1 <= 1'b0;
end
else begin
cnt_1 <= (cnt_1 == 4'd2)?4'd0:cnt_1+4'd1;
clk_1 <= (cnt_1 == 4'd2)?~clk_1:clk_1; //①
end
end
[email protected](negedge clk_150m or negedge rst_n) begin
if(!rst_n)
clk_2 <= 1'b0;
else
clk_2 <= (cnt_1 == 4'd1)?~clk_2:clk_2; //②
end
endmodule

②2N+1分频
clk1以原时钟clk的2N+1个周期为单位翻转,clk2在clk1时钟的中间点发生翻转
//时钟 相异或
module top #(
parameter NUM = 5
)(
input clk_150m,
input rst_n,
output clk_div3
);
reg [NUM-1:0] cnt_1;//上升沿计数
reg clk_1;
reg clk_2;
assign clk_div3 = clk_1 ^ clk_2;
[email protected](posedge clk_150m or negedge rst_n) begin
if(!rst_n) begin
cnt_1 <= 4'd0;
clk_1 <= 1'b0;
end
else begin
cnt_1 <= (cnt_1 == NUM-1)?0:cnt_1+1;
clk_1 <= (cnt_1 == NUM-1)?~clk_1:clk_1;
end
end
[email protected](negedge clk_150m or negedge rst_n) begin
if(!rst_n)
clk_2 <= 1'b0;
else
clk_2 <= (cnt_1 == (NUM-1)/2)?~clk_2:clk_2;
end
endmodule
五、两段式状态机
直接参照6.3 Verilog 状态机
两段式状态机设计:
- 首先,根据状态机的个数确定状态机编码。利用编码给状态寄存器赋值,代码可读性更好。
- 状态机第一段,时序逻辑,非阻塞赋值,传递寄存器的状态。
- 状态机第二段,组合逻辑,阻塞赋值,根据当前状态和当前输入,确定下一个状态机的状态,并确定输出信号。
六、三段式状态机
直接参照6.3 Verilog 状态机
两段式状态机设计:
- 首先,根据状态机的个数确定状态机编码。利用编码给状态寄存器赋值,代码可读性更好。
- 状态机第一段,时序逻辑,非阻塞赋值,传递寄存器的状态。
- 状态机第二段,组合逻辑,阻塞赋值,根据当前状态和当前输入,确定下一个状态机的状态。
- 状态机第三代,时序逻辑,非阻塞赋值,因为是 Mealy 型状态机,根据当前状态和当前输入,确定输出信号。
当然,使用的时候是更推荐三段式的!!
七、序列检测-状态机法
1. 概念
序列检测器就是将一个指定序列从数字码流中识别出来,并作出反馈。
2. 实验内容
本例中将设计一个“10010”序列的检测器。设X为数字码流的输入,Z为检测出标记输出,高电平表示发现指定的序列10010.
3. 模式图

4. RTL代码
module seqdet(
input clk ,
input rest_n ,
input inx ,
output outz
);
reg [2:0] state;
reg [2:0] next_state;
reg signal;
parameter IDLE = 3'b000,
DATA1= 3'b001,
DATA2= 3'b010,
DATA3= 3'b011,
DATA4= 3'b100,
DATA5= 3'b101;
always @(posedge clk or negedge rest_n) begin
if(!rest_n)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
case (state)
IDLE : next_state = inx ? DATA1 : IDLE;
DATA1 : next_state = inx ? DATA1 : DATA2;
DATA2 : next_state = inx ? DATA1 : DATA3;
DATA3 : next_state = inx ? DATA4 : IDLE;
DATA4 : next_state = inx ? DATA1 : DATA5;
DATA5 : next_state = IDLE;
default : ;
endcase
end
always @(posedge clk or negedge rest_n) begin
if(!rest_n)
signal <= 1'b0;
else
if(state == DATA5)
signal <= 1'b1;
else
signal <= 1'b0;
end
assign outz = signal;
endmodule
5. Testbench代码
`timescale 1ns/1ps
module test;
reg clk ;
reg rest_n;
reg inx ;
wire outz ;
seqdet U_seqdet(
.clk (clk ),
.rest_n (rest_n),
.inx (inx ),
.outz (outz )
);
initial begin
clk = 1'b0;
rest_n = 1'b0;
inx = 1'b0;
#12
rest_n = 1'b1;
#10
inx = 1'b1;
#10
inx = 1'b0;
#10
inx = 1'b1;
#10
inx = 1'b0;
#10
inx = 1'b0;
#10
inx = 1'b1;
#10
inx = 1'b0;
#10
inx = 1'b1;
#20
rest_n = 1'b0;
end
always #5 clk =~ clk;
endmodule
6. 时序仿真图

题外话:我在某公司的技术面上,有这样一道题目:
设计一个序列发生器,当start脉冲来时,连续发送三组“1011”序列。
module test(
input clk ,
input rst_n ,
input start ,
output pulse_out
);
reg [2:0] state;
reg [2:0] next_state;
reg [1:0] count;
reg signal;
parameter IDLE = 3'b000,
DATA1 = 3'b001,
DATA2 = 3'b010,
DATA3 = 3'b011,
DATA4 = 3'b100;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
state <= IDLE;
else
state <= next_state;
end
always @(*) begin
case(state)
IDLE : next_state = start ? DATA1 : IDLE;
DATA1 : next_state = DATA2;
DATA2 : next_state = DATA3;
DATA3 : next_state = DATA4;
DATA4 : next_state = (count < 2'd2) ? DATA1 : IDLE;
default: ;
endcase
end
always @(posedge clk or negedge rst_n) begin
if(!rst_n)
count <= 2'd0;
else
if(state == DATA4) begin
count <= count + 1'b1;
if(count == 2'd2)
count <= 2'd0;
end
end
always @(*) begin
case(state)
IDLE : signal = 1'bx;
DATA1 : signal = 1'b1;
DATA2 : signal = 1'b0;
DATA3 : signal = 1'b1;
DATA4 : signal = 1'b1;
default : ;
endcase
end
assign pulse_out = signal;
endmodule
Testbench代码:
`timescale 1ns/1ps
module tb_test;
reg clk ;
reg rst_n ;
reg start ;
wire pulse_out;
test u_test(clk,rst_n,start,pulse_out);
initial begin
clk = 1'b0;
rst_n = 1'b0;
start = 1'b0;
#10
rst_n = 1'b1;
#30
start = 1'b1;
#20
start = 1'b0;
#300
start = 1'b1;
end
always #5 clk =~ clk;
endmodule

八、序列检测-移位寄存器
1. 概念
序列检测器就是将一个指定序列从数字码流中识别出来,并作出反馈。
2. 实验内容
本例中将设计一个“10010”序列的检测器。设X为数字码流的输入,Z为检测出标记输出,高电平表示发现指定的序列10010.
3. 原理图

4. RTL代码
module seqdet
(
input wire x ,
input wire clk ,
input wire rst ,
output wire z ,
output reg [4:0] q
);
assign z = (q == 5'b10010) ? 1'b1:1'b0;
always @ (posedge clk,negedge rst)
if(!rst)
q <= 5'd0;
else
q <= {
q[3:0],x};
endmodule
5. Testbench代码
`timescale 1ns/1ns
module seqdet_tb;
localparam T =20;
reg clk,rst;
reg [23:0] data;
wire z,x;
wire [4:0] q;
assign x = data[23];
initial
begin
clk =0;
rst =1;
#2 rst =0;
#30 rst =1;
data =20'b1100_1001_0000_1001_0100;
#(T*1000) $stop;
end
always #T clk = ~clk;
always @ (posedge clk)
#2 data = {data[22:0],data[23]};
seqdet U1
(
.x(x),
.z(z),
.clk(clk),
.q(q),
.rst(rst)
);
endmodule
6. 时序仿真图
需要说明一点:看着寄存器法的代码量要比状态机法的少,但综合后的图并不一定会更简单。
九、4×4查表乘法器
1. 概念
查找表乘法器先将乘法的所有可能结果存储起来,然后将两个相乘的数据组合起来作为“地址”找到相应的结果。例如:
设A,B为两个2位二进制数,则A,B各有4种取值可能,乘积有4*4=16种可能(排除重复的其实只有8种可能),我们先将{A,B}对应的16种可能结果存储起来,然
后对于每一特点的输入组合{A,B},找到对应的输出即可。该方式速度很快,只取决于读取存储器的速度,但是预存结果要占用较多资源,因此是面积换取速度
思想的体现。
同时,随着乘数位宽的增加,需要存储的结果迅速增加,不利于实现,因此该方式适用于位宽很小的情况。但是我们可以将高位宽的数据分解成低位宽的数据再调用查找表乘法器。例如:
设A,B为两个8位数据,可将A分解为A=A1×16+A2,其中A1为高4位,A2为低4位;同理B=B1×16+B2,然后
A×B=(A1×16+A2)×(B1×16+B2)=A1*B1*16*16 + A1*B2*16 + A2*B1*16 + A2*B2
这样就将两个8位数的相乘转化为4组4位数相乘,然后再相加,其中乘以常数可以通过移位运算实现。对于2N位数据A,可分解为 A=A1×2^N+A2.
2. RTL代码
module lookup44(
input [3:0] dina ,
input [3:0] dinb ,
input clk ,
output reg [7:0] dout
);
reg [1:0] a1,a2,b1,b2;
wire [3:0] dout1,dout2,dout3,dout4;//输入数据拆为高2位和低2位分别缓存
always @ ( posedge clk )begin
a1<=dina[3:2];
a2<=dina[1:0];
b1<=dinb[3:2];
b2<=dinb[1:0];
end
//将4个乘法运算的结果相加输出
always @ ( posedge clk )begin
dout<=(dout1<<4)+(dout2<<2)+(dout3<<2)+dout4;
end
//调用2位查找表乘法器
lookup22 mul1(a1,b1,clk,dout1),
mul2(a2,b1,clk,dout2),
mul3(a1,b2,clk,dout3),
mul4(a2,b2,clk,dout4);
endmodule
module lookup22(
input [1:0] dina ,
input [1:0] dinb ,
input clk ,
output reg [3:0]dout
);
always @ ( posedge clk )begin
case({
dina,dinb})
4'b0000:dout<=0;
4'b0001:dout<=0;
4'b0010:dout<=0;
4'b0011:dout<=0;
4'b0100:dout<=0;
4'b0101:dout<=1;
4'b0110:dout<=2;
4'b0111:dout<=3;
4'b1000:dout<=0;
4'b1001:dout<=2;
4'b1010:dout<=4;
4'b1011:dout<=6;
4'b1100:dout<=0;
4'b1101:dout<=3;
4'b1110:dout<=6;
4'b1111:dout<=9;
endcase
end
endmodule
3. Testbench代码
`timescale 1ns/1ns
module test;
reg [3:0] dina;
reg [3:0] dinb;
reg clk ;
wire [7:0]dout;
lookup44 u_lookup44(
.dina (dina),
.dinb (dinb),
.clk (clk ),
.dout (dout)
);
integer i,j;
initial begin
clk = 1'b0;
dina = 4'd0;
dinb = 4'd0;
for(i=0;i<16;i=i+1) begin
#10 dinb = 4'd0;
for(j=0;j<16;j=j+1) begin
#10 dinb = dinb + 1'b1;
end
#10 dina = dina + 1'b1;
end
end
always #5 clk =~ clk;
endmodule
4. 时序仿真图

十、最低位1检测
我也百度了一些资料,但这些都是对8位数据这样而言的,可以用 casez 语句啥的来写,但是一旦位数比较高的话,就会比较麻烦了,比如128位这样的。当然啦,也是可以用for语句来进行循环,但考虑到综合性不够好,就还是暂且不用。我想到的一个是用移位寄存器来实现,但现在还没实现,等我后面实现了再来更新。
十一、两个八位数相乘
1. RTL代码
module mul_4x4(
input [7:0] A,
input [7:0] B,
output [15:0] C
);
wire [15:0] shift0;
wire [15:0] shift1;
wire [15:0] shift2;
wire [15:0] shift3;
wire [15:0] shift4;
wire [15:0] shift5;
wire [15:0] shift6;
wire [15:0] shift7;
assign shift0 = A[0] ? {
8'b0000_0000,B}:{
16'b0000_0000_0000_0000};
assign shift1 = A[1] ? {
7'b0000_000,B,1'b0}:{
16'b0000_0000_0000_0000};
assign shift2 = A[2] ? {
6'b0000_00,B,2'b00}:{
16'b0000_0000_0000_0000};
assign shift3 = A[3] ? {
5'b0000_0,B,3'b000}:{
16'b0000_0000_0000_0000};
assign shift4 = A[4] ? {
4'b0000,B,4'b0000}:{
16'b0000_0000_0000_0000};
assign shift5 = A[5] ? {
3'b000,B,5'b0000_0}:{
16'b0000_0000_0000_0000};
assign shift6 = A[6] ? {
2'b00,B,6'b0000_00}:{
16'b0000_0000_0000_0000};
assign shift7 = A[7] ? {
1'b0,B,7'b0000_000}:{
16'b0000_0000_0000_0000};
assign C = shift0 + shift1 + shift2 +shift3 +shift4 +shift5 +shift6 +shift7;
//移位相加的形式 B*A[0]*2^0 + B*A[1]*2^1 + B*A[2]*2^2 + B*A[3]*2^3
endmodule
2. Testbench代码
`timescale 1ns/1ns
module test;
reg [7:0] A;
reg [7:0] B;
wire [15:0] C;
mul_4x4 u_mul_4x4(A,B,C);
integer i,j;
initial begin
A = 8'd0;
B = 8'd0;
#10
for(i = 0;i < 256 ; i= i + 1) begin
#10 A = 8'd0;
for(j=0;j<256;j=j+1) begin
#10 A = A + 8'd1;
end
#10 B = B + 8'd1;
end
end
endmodule
3. 时序仿真图

十二、十进制计数器
1. 异步十进制计数器
1) RTL代码
module count10 (
input clk ,
input rst_n ,
output reg[3:0] Q
);
wire Q0,Q1,Q2,Q3;
JK_FF JK0(clk,rst_n,1'b1,1'b1,Q0),
JK1(Q0,rst_n,~Q3,1'b1,Q1),
JK2(Q1,rst_n,1'b1,1'b1,Q2),
JK3(Q0,rst_n,Q1&Q2,1'b1,Q3);
always @(*) begin
Q = {
Q3,Q2,Q1,Q0};
end
endmodule
module JK_FF (
input clk ,
input rst_n ,
input J ,
input K ,
output reg Q
);
always @(negedge clk) begin
if(!rst_n)
Q <= 1'b0;
else begin
case ({
J,K})
2'b00:Q <= Q; //状态保持不变
2'b01:Q <= 1'b0; //置0
2'b10:Q <= 1'b1; //置1
2'b11:Q <= ~Q; //翻转
default: ;
endcase
end
end
endmodule
2)Testbench代码
`timescale 1ns/1ns
module test;
reg clk ;
reg rst_n ;
wire [3:0] Q ;
count10 u_count10(
.clk (clk ),
.rst_n (rst_n ),
.Q (Q )
);
initial begin
clk = 1'b0;
rst_n = 1'b0;
#10
rst_n = 1'b1;
end
always #5 clk =~ clk;
endmodule
3)时序仿真图

2. 同步十进制计数器
1)RTL代码
module count10 (
input clk ,
input rst_n ,
output reg[3:0] Q
);
wire Q0,Q1,Q2,Q3;
JK_FF JK0(clk,rst_n,1'b1,1'b1,Q0),
JK1(clk,rst_n,Q0&(~Q3),Q0,Q1),
JK2(clk,rst_n,Q0&Q1,Q0&Q1,Q2),
JK3(clk,rst_n,Q0&Q1&Q2,Q0,Q3);
always @(*) begin
Q = {
Q3,Q2,Q1,Q0};
end
endmodule
module JK_FF (
input clk ,
input rst_n ,
input J ,
input K ,
output reg Q
);
always @(negedge clk) begin
if(!rst_n)
Q <= 1'b0;
else begin
case ({
J,K})
2'b00:Q <= Q; //状态保持不变
2'b01:Q <= 1'b0; //置0
2'b10:Q <= 1'b1; //置1
2'b11:Q <= ~Q; //翻转
default: ;
endcase
end
end
endmodule
后面的文件和异步的是一样的。
总结
代码为主!
仅供学习!如有错误,还望指正!
边栏推荐
- 双击事件与单击事件的那些事
- [untitled]
- 50 lectures on practical application of R language (34) - practical application cases of curve separation (with R language code)
- Centos7 installation php7.2
- LabVIEW jump to web page
- Quick sort, query the k-largest number of the sequence
- Tortoise does not display a green Icon
- Map and set use pari as the key value. How to define
- 2022年启牛学堂证券开户安全的嘛?
- 兰宝传感科技冲刺科创板:年营收3.5亿 许永童家族色彩浓厚
猜你喜欢

层次分析法(AHP)

Allegro's method of setting network flying line and network color

今日直播|Apache Pulsar x KubeSphere 在线 Meetup 火热来袭

【一起上水硕系列】最简单的字幕配置
![[linear algebra] 1.1 second and third order determinants](/img/ea/70b59c64d3287a887e371a9181fe45.png)
[linear algebra] 1.1 second and third order determinants

EMC、EMI、EMS的关系

How does sound amplify weak sounds

allegro对走好的线取消走线的方法

认证培训|StreamNative Certification 培训第2期

LabVIEW jump to web page
随机推荐
Basic MySQL database operations
2022-2028 global bubble CPAP system industry survey and trend analysis report
The method of displaying or closing the network flying line in Allegro design
今日直播|Apache Pulsar x KubeSphere 在线 Meetup 火热来袭
[untitled]
2022-2028 global low carbon concrete industry research and trend analysis report
Leetcode counts the number of ways to place houses
PWN攻防世界guess_num
1110: 最近共同祖先(函数专题)
2022-2028 global pneumatic test probe industry survey and trend analysis report
ArrayList basic operation and add element source code
2022-2028 global secondary butyl lithium industry research and trend analysis report
leetcode 统计放置房子的方式数
Matrix eigenvalue and eigenvector solution - eigenvalue decomposition (EVD)
Lanbao sensor technology rushes to the scientific innovation board: annual revenue of 350million yuan xuyongtong family has a strong color
[线性代数] 1.2 全排列和对换
如何在关闭socket连接的时候跳过TIME_WAIT的等待状态
There's a mystery behind the little login
微信小程序安全登录,必须先校验微信返回openid,确保信息唯一性。
2022-2028 global UAV detection radar industry research and trend analysis report
