4.RTL经典代码
4.RTL经典代码
1.Verilog 实现串并转换
- lsb优先
- msb优先
input clk, rst_n, data_i;
output [7:0] data_o;
module Serialize(
input clk,
input rst_n,
input data_i,
output reg[7:0] data_o
);
// lsb first
always@(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data_o <= 8'b0;
end
else begin
data_o <= {data[6:0],data_i};
end
end
// msb first
reg [2:0] cnt;
always@(posedge clk or negedge rst_n)begin
if(~rst_n)begin
data_o <= 8'b0;
cnt<=3'd0;
end
else begin
data_o[7-cnt] <= data_i;
cnt <= cnt + 1'b1;
end
end
endmodule
2.异步双端口RAM
题目要求:深度为16,位宽为8bit。A口读出,B口写入。支持片选,读写请求,要求代码可综合。
module dpram_16x18(
input clk_a,
input [3:0] addr_a,
input [7:0] dout_a,
\\...
input clk_b,
input [7:0] din_b,
input [3:0] addr_b,
\\...
);
endmodule
module Dula_Port_Sram
#(
paramemter ADDR_WIDTH = 4,
paramemter DATA_WIDTH = 8,
paramemter DATA_DEPTH = 1 << ADDR_WIDTH
)
(
input clka,
input clkb,
input rst_n,
input csen_n, // 使能模块与否
// Port A Signal 读
input [ADDR_WIDTH-1:0] addra,
output reg [DATA_WIDTH-1:0] data_a,
input rdena_n,
// Port B Signal 写
input [ADDR_WIDTH-1:0] addrb,
input [DATA_WIDTH-1:0] data_b,
input wrenb_n,
);
integer i;
reg [DATA_WIDTH-1:0] register[0:DATA_DEPTH-1];
always @(posedge clkb or negedge rst_n)begin
if(~rst_n)begin
for(i=0;i<DATA_DEPTH;i=i+1)
register[i]<=8'b0;
end
else if(~wrenb_n && csen_n == 1'b0)
register[addrb]<= data_b;
end
always @(posedge clka or negedge rst_n) begin
if(~rst_n)begin
data_a <=0;
end
else if(rdena_n == 1'b0 &&& csen_n ==1'b0)
data_a <= register[addra];
else
data_a <= data_a;
end
endmodule
3.Verilog 三分频电路且占空比为50%
module Div_three(
input clk,
input rst_n,
output div_three
);
reg [1:0] cnt;
reg div_clk1;
reg div_clk2;
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
cnt <=0;
end
else if(cnt == 2)
cnt <=0;
else begin
cnt <= cnt + 1'b1;
end
end
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
div_clk1 <= 0;
end
else if(cnt == 0)begin
div_clk1 <= ~div_clk1;
end
else begin
div_clk1 <= div_clk1;
end
end
always @(negedge clk or negedge rst_n)begin
if(~rst_n)begin
div_clk2 <= 0;
end
else if(cnt == 2)begin
div_clk2<=~div_clk2;
end
else
div_clk2 <= div_clk2;
end
assign div_three = div_clk2 ^ div_clk1;
endmodule
echo "开始编译"
iverilog -o wave Div_three.v tb_Div_three.v
echo "编译完成"
vvp -n wave -lxt2
echo "打开波形文件"
gtkwave wave.vcd
`timescale 1ns / 1ps
module tb_Div_three;
// Parameters
parameter CLK_PERIOD = 10; // Clock period
// Signals
reg clk;
reg rst_n;
wire div_three;
// Instantiate the Div_three module
Div_three uut (
.clk(clk),
.rst_n(rst_n),
.div_three(div_three)
);
// Clock generation
initial begin
clk = 0;
forever #(CLK_PERIOD / 2) clk = ~clk; // Toggle clock
end
initial //启动 iverilog
begin
$dumpfile("wave.vcd"); //生成的vcd文件名称
$dumpvars(0, tb_Div_three); //tb模块名称
end
// Test sequence
initial begin
// Initialize signals
rst_n = 0; // Active low reset
#15; // Wait for a few clock cycles
rst_n = 1; // Release reset
// Run for a while
#100; // Simulate for 100 time units
// Finish the simulation
$finish;
end
endmodule
4.glitch free 时钟切换电路
https://blog.csdn.net/bleauchat/article/details/96180815
要求:输入 sel ,clka ,clkb ,sel为1输出clka, sel为0输出clkb
有毛刺时钟切换电路
源时钟相互倍数关系,下时钟开关出现毛刺的解决
-- 在时钟源的选择路径中增加 负边沿D触发器
reg out1,out2;
always @(negedge CLK1 or negedge rst_n)begin
if(~rst_n)begin
out1 <=0;
end
else begin
out1<= SELECT & ~out2;
end
end
always@(negedge CLK0 or negedge rst_n)begin
if(~rst_n)begin
out2 <=0;
end
else begin
out2<=~SELECT & ~out1;
end
end
assign OUTPUT_CLK= (CLK1 & out1)|(CLK0 & out2);
针对无关时钟的毛刺保护
在上边的基础上,再添加一个额外级的正边沿触发触发器来提供针对亚稳态性的保护

5.异步复位同步释放电路
module Sys_Rst(
input clk,
input rst,
output sys_rst
);
reg rst_r0,rst_r1;
always @(posedge clk or posedge rst) begin
if(rst)begin
rst_r0 <= 1'b1;
rst_r1 <= 1'b1;
end
else begin
rst_r0 <= rst;
rst_r1 <= rst_r0;
end
end
assign sys_rst = rst_r1;
endmodule
6.按键消抖电路
module debounce(
input clk,
input rst_n,
input key_in,
output key_flag
);
paramemter JITTER = 240;// 12 MHz / (20 ms)
reg [1:0] key_r;
wire change;
reg [15:0] delay_cnt;
always @(posedge clk or negedge rst_n )begin
if(~rst_n)begin
key_r <= 0;
end
else begin
key_r <= {key_r[0],key_in};
end
end
//检测按键前一个状态与后一个状态是否一致, 一致的话,change为0,计数;
// 如不不一致,是为抖动,不计数。
assign change = (~key_r[1] & key_r[0]) | (key_r[1] & ~key_r[0]);
always @(posedge clk or negedge rst_n) begin
if(~rst_n)begin
end
else if (change == 1'b1)
delay_cnt <=0;
else if(delay_cnt == JITTER)
delay_cnt <= delay_cnt;
else
delay_cnt <= delay_cnt + 1;
end
assign key_flag = ((delay_cnt == JITTER - 1 ) && (key_in == 1'b1))? 1'b1:1'b0;
endmodule
7.同步FIFO 深度为16 ,数据位宽为8bit
https://www.asic-world.com/examples/verilog/syn_fifo.html
https://www.nowcoder.com/discuss/498514402438553600?sourceSSR=search
// 返回以2为底的n的对数
function integer clog2 (input integer n); begin
n = n - 1;
for (clog2 = 0; n > 0; clog2 = clog2 + 1)
n = n >> 1;
end
endfunction
module syn_fifo #(
//## 深度为16 ,数据位宽为8bit
parameter WIDTH = 8,
parameter DEPTH = 16
)
(
input clk,
input rst_n,
input winc, //写使能
input [WIDTH-1:0] wdata, //写数据
input rinc, //读使能
output wire [WIDTH-1:0] rdata,
output reg wfull,
output reg rempty
);
// 用localparam
localparam ADDR_WIDTH = $clog2(DEPTH);
reg [ADDR_WIDTH:0] waddr;
reg [ADDR_WIDTH:0] raddr;
// FIFO写入
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
waddr<='b0;
end
else begin
if(winc && ~ wfull)begin
waddr <= waddr + 1'b1;
end
else begin
waddr <= waddr;
end
end
end
//FIFO 读
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
raddr<=0;
end
else begin
if(rinc && ~ rempty)begin
raddr <= raddr + 1'b1;
end
else begin
raddr <= raddr;
end
end
end
// 空满标志判断
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
wfull <= 1'b0;
rempty <= 1'b0;
end
else begin
wfull <=(raddr == {~waddr[ADDR_WIDTH],waddr[ADDR_WIDTH-1:0]});
rempty<=(raddr == waddr);
end
end
// 带有 parameter 例化双口RAM
dual_port_RAM
#(
.DEPTH(DEPTH),
.WIDTH(WIDTH)
)
dual_port_RAM_U0
(
.wclk(clk),
.wenc(winc),
.waddr(waddr[ADDR_WIDTH-1:0]),
.wdata(wdata),
.rclk(clk),
.renc(rinc),
.raddr(raddr[ADDR_WIDTH-1:0]),
.rdata(rdata)
);
endmodule
/**************RAM 子模块*************/
module dual_port_RAM #(parameter DEPTH = 16,
parameter WIDTH = 8)(
input wclk
,input wenc
,input [$clog2(DEPTH)-1:0] waddr //深度对2取对数,得到地址的位宽。
,input [WIDTH-1:0] wdata //数据写入
,input rclk
,input renc
,input [$clog2(DEPTH)-1:0] raddr //深度对2取对数,得到地址的位宽。
,output reg [WIDTH-1:0] rdata //数据输出
);
reg [WIDTH-1:0] RAM_MEM [0:DEPTH-1];
always @(posedge wclk) begin
if(wenc)
RAM_MEM[waddr] <= wdata;
end
always @(posedge rclk) begin
if(renc)
rdata <= RAM_MEM[raddr];
end
endmodule

module tb_syn_fifo ();
reg clk,rst_n,winc,rinc;
reg [7:0]wdata;
wire wfull,rempty;
wire[7:0] rdata;
syn_fifo #(
.WIDTH (8),
.DEPTH (16)
)
u0(
clk ,
rst_n ,
winc ,
wdata ,
rinc ,
rdata ,
wfull ,
rempty
);
initial
begin
$dumpfile("wave.vcd"); //生成的vcd文件名称
$dumpvars(0, tb_syn_fifo); //tb模块名称
end
integer i;
initial begin
#0;
clk=0;
rst_n=0;
#10
rst_n=1;
end
always #5 clk=~clk;
initial begin
#0;
winc=0;
rinc=0;
wdata=0;
end
initial begin
#20;
send_wr;
end
initial begin
#100
send_rd;
#1000;
$finish;
end
task send_wr;
begin
for (i=0;i<8;i=i+1)
begin
@(posedge clk )begin
winc<=1;
wdata<=i+1;
end
end
@(posedge clk)begin
winc<=0;
wdata<=0;
end
repeat(10)@(posedge clk);
end
endtask
task send_rd;
begin
for(i=0;i<8;i=i+1)
begin
@(posedge clk)begin
rinc<=1;
end
@(posedge clk)begin
rinc<=0;
end
end
end
endtask
endmodule
8.异步复位串联T触发器
https://blog.nowcoder.net/n/297478e0500047958c87d4a126060f1d
9.三个数中的中位数
三个8bit的无符号数a,b,c,请选择其中的中位数,资源消耗尽可能小
https://blog.csdn.net/luoai_2666/article/details/120382208

module median_finder (
input [7:0] a,
input [7:0] b,
input [7:0] c,
output reg [7:0] median
);
always @(*) begin
// Determine the median based on comparisons
if ((a >= b && a <= c) || (a <= b && a >= c)) begin
median = a; // a is the median
end
else if ((b >= a && b <= c) || (b <= a && b >= c)) begin
median = b; // b is the median
end
else begin
median = c; // c is the median
end
end
endmodule
10.当前序列值被三整除(未确定)
输入一串序列,每次一个bit,最新的输入值落在序列的最低位,输出当前序列值能否被三整除,若是可以,则输出1,否则输出0
例子: 1011, 输入序列值为 1,3,3,11, 输出为 0110 类推
module div_by_3_sequence(
input clk, // 时钟信号
input rst, // 复位信号
input bit_in, // 输入比特
output reg [7:0] result // 输出序列,8位宽
);
reg [3:0] current_value; // 当前四位二进制值
reg [2:0] count; // 计数器,用于输出序列位数
// 初始状态
initial begin
current_value = 4'b0000; // 初始化序列值为0
result = 8'b00000000; // 初始化输出序列为0
count = 0; // 初始化计数器
end
always @(posedge clk or posedge rst) begin
if (rst) begin
current_value <= 4'b0000; // 复位时序列值为0
result <= 8'b00000000; // 复位输出序列
count <= 0; // 复位计数
end else begin
// 将新的比特输入到序列的最低位
current_value <= (current_value << 1) | bit_in;
// 将可被3整除作为输出序列的一部分
if (current_value % 3 == 0) begin
result[count] <= 1; // 可被3整除,输出为1
end else begin
result[count] <= 0; // 不可被3整除,输出为0
end
// 更新计数器,确保输出不超过8位
count <= (count < 7) ? count + 1 : count; // 最大计数为7,确保result为8位宽
end
end
endmodule
11. 卡诺图化简
写出真值表,卡诺图,以及最简表达式

有:
A | B | C | Z |
---|---|---|---|
0 | 0 | 0 | 1 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 0 |
1 | 0 | 0 | 1 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 0 |
12.滤除小于两个时钟周期的毛刺信号
将持续时间少于 8 个时钟周期的信号毛刺全部滤除,输出变成了一个较为干净的电平信号。作为扩展,我们还可以通过调节计数器的阈值改变该电路的滤除精度
module Jitter_Clear(
input clk,
input btn,
output btn_clean
);
reg [3:0] cnt;
always @(posedge clk) begin
if (!btn)
cnt <= 4'h0;
else if (cnt < 4'h8)
cnt <= cnt + 1'b1;
end
assign btn_clean = cnt[3];
endmodule
13.加法与进位
https://hdlbits.01xz.net/wiki/Exams/ece241_2014_q1c
https://blog.csdn.net/wangkai_2019/article/details/106664283
https://blog.csdn.net/wangkai_2019/article/details/106209850

14.2-4译码器
https://1592289828.spaces.eepw.com.cn/articles/article/item/323142

// e为低电平使能的2-4译码器
module test(
input a ,
input b ,
input e ,
output wire [3:0] y
);
assign y[0] = ~(~a & ~b & ~e);
assign y[1] = ~(~a & b & ~e);
assign y[2] = ~( a & ~b & ~e);
assign y[3] = ~( a & b & ~e);
endmodule
15.“101”序列检测器
module top(
input clk,
input rst_n,
input data,
output flag_101
)
parameter S0 = 0,
S1 = 1,
S2 = 2,
S3 = 3;
reg [1:0] state;
always @(posedge clk or negedge rst_n)begin
if(rst_n == 1'b0)begin
state <= S0;
end
else begin
case(state)
S0:
if(data == 1)
state <= S1;
else
state <= S0;
S1:
if(data == 0)
state <= S2;
else
state <= S1;
S2:
if(data == 1)
state <= S3;
else
state <= S0;
S3:
if(data == 1)
state <= S1;
else
state <= S2;
default:
state <=S0;
endcase
end
end
assign flag_101 = (state == S3)? 1'b1: 1'b0;
endmodule