我是靠谱客的博主 糟糕小懒虫,最近开发中收集的这篇文章主要介绍整理经典verilog笔试题/面试手撕代码信号跨时钟域处理序列检测:1011时钟分频7进制循环计数器(可赋初值)4位全加器边沿检测,输出一个周期宽度的脉冲串并转换实现一个异步双端口RAM,觉得挺不错的,现在分享给大家,希望可以做个参考。
概述
这里写自定义目录标题
- 信号跨时钟域处理
- 单个电平信号的时钟域跨越
- 单个脉冲信号的时钟域跨越
- 多比特总线的时钟域跨越
- 序列检测:1011
- 使用状态机实现
- 使用移位寄存器实现
- 时钟分频
- 7进制循环计数器(可赋初值)
- 4位全加器
- 边沿检测,输出一个周期宽度的脉冲
- 串并转换
- 实现一个异步双端口RAM
信号跨时钟域处理
单个电平信号的时钟域跨越
起点时钟域由触发器来驱动信号的离开(过滤毛刺)
终点时钟域采用两个或多个触发器串联
always @ (posedge clk or negedge rst)
begin
if(!rst)
begin
data_in_1 <= 1'b0;
data_in_2 <= 1'b0;
out <= 1'b0;
end
else begin
data_in_1 <= data_in;
data_in_2 <= data_in_1;
out <= data_in_2;
end
end
单个脉冲信号的时钟域跨越
起点时钟域将脉冲转电平变化
终点时钟域使用双触发器同步,并随后检测电平变化,转换成脉冲。
//脉冲转电平
always @ (posedge clk or negedge rst)
begin
if(!rst)
out <= 0;
else if(data_in)
out <= ~out;
else
out <= out;
end
//电平变化检测
//电平变化检测
always @ (posedge clk, negedge rst)
begin
if(!rst)
data_in_1 <= 0;
else begin
data_in_1 <= data_in;
end
end
assign out = data_in ^ data_in_1;
多比特总线的时钟域跨越
若按照单比特方法,将其按照总线位宽展开,在起点时钟域中与时钟边沿对其的数据,在到达终点时钟域后可能产生数据错位
1.格雷编码的计数器:传输计数器值可以这样
2.异步FIFO:包括简单双口读写RAM、读指针、写指针(读写指针就是顺序累加的计数器)
3.翻转同步器做数据选通脉冲:异步FIFO需要资源较多
序列检测:1011
使用状态机实现
module serchek(
input clk,
input rst,
input in,
output out
);
reg [2:0] now_state, next_state;
parameter IDLE = 3'b000, A = 3'b001, B = 3'b011, C = 3'b010, D = 3'b110;
always @ (posedge clk, negedge rst) //时序部分
begin
if(!rst)
now_state <= IDLE;
else
now_state <= next_state;
end
always @ (*) //组合逻辑部分
begin
case (now_state)
IDLE: if(in) next_state = A;
else next_state = IDLE;
A: if(in) next_state = A;
else next_state = B;
B: if(in) next_state = C;
else next_state = IDLE;
C: if(in) next_state = D;
else next_state = B;
D: if(in) next_state = A;
else next_state = IDLE;
endcase
end
always @ (posedge clk, negedge rst) //时序产生输出
begin
if(!rst)
out <= 0;
else
if(now_state == D)
out <= 1;
end
assign out = (rst) ? 0 : (now_state == D) ? 1 : 0; //组合逻辑产生输出
endmodule
使用移位寄存器实现
module serchek(
input clk,
input rst,
input din,
output reg flag
)
reg [3:0] data_re;
always @ (posedge clk, negedge rst)
begin
if(!rst)
data_re <= 4'd0;
else begin
data_re <= {data_re[2:0], din};
end
end
assign flag = (data_re == 4'b1011) ? 1'b1 : 1'b0;
endmodule
时钟分频
module div(
input clk,
input rst,
output clk_out
)
//------------------ 二分频(两个周期变成一个周期)------------
//--------------------------------------------------------------
reg clk_out;
always @ (posedge clk, negedge rst)
begin
if(!rst)
clk_out <= 0;
else
clk_out <= ~clk_out;
end
//------------------ 三分频(三个周期变成一个周期)------------
//-----------------------占空比2/3------------------------------
reg clk_out;
reg [1:0]count;
always @ (posedge clk, negedge rst)
begin
if(!rst)
begin
clk_out <= 0;
count <= 2'b00; //利用计数器进行信号翻转计数
end
else if (count == 0)
begin
clk_out <= ~clk_out;
count <= count + 1'b1; //!!!!!!
end
else if (count == 2)
begin
clk_out <= ~clk_out;
count <= 2'b00;
end
else begin
count <= count + 1'b1;
end
//------------------ 三分频(三个周期变成一个周期)------------
//-----------------------占空比1/2------------------------------
//---------利用两个占空比为1/3(上升沿和下降沿)进行或操作----
reg clk_out, clk1, clk2;
reg [1:0]count1, count2;
always @ (posedge clk, negedge rst) //上升沿触发
begin
if(!rst)
begin
clk1 <= 1'b0;
count1 <= 2'b00;
end
else if(count1 == 0)
begin
clk1 <= ~clk1;
count1 <= count1 + 2'b01;
end
else if(count1 == 1)
begin
clk1 <= ~clk1;
count1 <= count1 + 2'b01;
end
else if(count1 == 2) // 直接else begin即可。
begin
count1 <= 2'b00;
end
end
always @ (negedge clk, negedge rst) //下降沿触发
begin
if(!rst)
begin
clk2 <= 1'b0;
count2 <= 2'b00;
end
else if(count2 == 0)
begin
clk2 <= ~clk1;
count2 <= count2 + 2'b01;
end
else if(count2 == 1)
begin
clk2 <= ~clk1;
count2 <= count2 + 2'b01;
end
else if(count2 == 2) // 直接else begin即可。
begin
count2 <= 2'b00;
end
end
assign clk_out = clk1 | clk2;
endmodule
7进制循环计数器(可赋初值)
module count(
input clk,
input rst,
input [2:0]init,
input en,
output [2:0] y
)
reg [2:0] y;
always @ (posedge clk, negedge rst)
begin
if(!rst)
y <= 3'b000;
else begin
if(en)
y <= init;
else if(y == 3'b110) //应该改成 >= 判断,防止初始值超过阈值
y <= 3'b000;
else
y <= y + 3'b001;
end
end
end
4位全加器
module adder(
input clk,
input rst,
input [3:0] a,b;
input ci, //包含输入的进位和输出的进位
output [3:0] y,
output co
)
always @ (posedge clk, negedge rst)
begin
if(!rst)
{co,y} <= 5'b00000;
else begin
{co,y} <= a + b + ci;
end
end
边沿检测,输出一个周期宽度的脉冲
module edge_detect(
input clk,
input rst,
input data,
output pos_det,
output neg_det,
output posneg_det
)
reg [1:0] data_re; //对输入的连续两个信号进行保留
always @ (posedge clk, negedge rst)
begin
if(!rst)
data_re <= 2'b00;
else
data_re <= {data_re[0], data};
end
assign pos_det = ~data_re[1] & data_re[0]; //对上升沿的判断
assign neg_det = data_re[1] & ~data_re[0];
assign posneg_det = pos_det | neg_det;
endmodule
串并转换
//--------------------------- 串行转并行(4bit) -----------------
module ser2pal(
input clk,
input rst,
input in,
output [3:0] out_lsb,
output [3:0] out_msb
)
always @ (posedge clk, negedge rst)
begin
if(!rst)
begin
out_lsb <= 4'b0000;
out_msb <= 4'b0000;
end
else begin
out_lsb <= {out_lsb[2:0], in};
out_msb <= {in, out_msb[3:1]};
end
end
end
//--------------------------- 并行转串行(4bit) -----------------
module pal2ser (
input clk,
input rst,
input [3:0] in,
input en, //需要一个使能将并行数据置位一下
output out
)
rsg [3:0] in_re;
always @ (posedge clk, negedge rst)
begin
if(!rst)
in_re <= 4'b0000;
else begin
if(en)
in_re <= in;
else
in_re <= in_re << 1;
end
assign out = in_re[3];
endmodule
实现一个异步双端口RAM
深度为16,位宽为8bit,A口读,B口写,支持片选、读写请求
module ram_16x8
#(
parameter ADDR_WIDTH = 4,
parameter DATA_WIDTH = 8,
parameter NUMBER_REG = 1 << ADDR_WIDTH
)
(
input rst,
input csen,
input clk1,
input ren,
input [ADDR_WIDTH-1:0] addr1,
output reg [DATA_WIDTH-1:0] dout1,
input clk2,
input wen,
input [ADDR_WIDTH-1:0] addr2,
input [DATA_WIDTH-1:0] din2
);
reg [DATA_WIDTH-1:0] register [NUMBER_REG-1:0];
integer i;
always @ (posedge clk1, negedge rst)
begin
if(!rst)
dout1 <= 0;
else if(ren && !csen)
dout1 <= register[addr1];
else
dout1 <= dout1;
end
always @ (posedge clk2 or negedge rst)
begin
if(!rst) begin //对整个ram进行初始化
for(i=0;i<NUMBER_REG;i=i+1) begin
register[i] <= 0;
end
end
else begin
if(wen && csen)
register[addr2] <= din2;
end
end
最后
以上就是糟糕小懒虫为你收集整理的整理经典verilog笔试题/面试手撕代码信号跨时钟域处理序列检测:1011时钟分频7进制循环计数器(可赋初值)4位全加器边沿检测,输出一个周期宽度的脉冲串并转换实现一个异步双端口RAM的全部内容,希望文章能够帮你解决整理经典verilog笔试题/面试手撕代码信号跨时钟域处理序列检测:1011时钟分频7进制循环计数器(可赋初值)4位全加器边沿检测,输出一个周期宽度的脉冲串并转换实现一个异步双端口RAM所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复