经典题"/>
Verilog经典题
目录
- 1.快时钟到慢时钟 检测单时钟脉冲信号
- 1.2 带有握手信号的脉冲同步
- 2. 异步复位同步释放
- 3.握手信号跨时钟域
- 4.小数分频
- 5 状态机
- 5.1 售货机
1.快时钟到慢时钟 检测单时钟脉冲信号
sig_a 是 clka(300M)时钟域的一个单时钟脉冲信号(高电平持续一个时钟clka周期),请设计脉冲同步电路,将sig_a信号同步到时钟域 clkb(100M)中,产生sig_b单时钟脉冲信号(高电平持续一个时钟clkb周期)输出。请用 Verilog 代码描述。
clka时钟域脉冲之间的间隔很大,无需考虑脉冲间隔太小的问题
不考虑间隔的问题,将源时钟域的脉冲信号转换成电平信号。对同步的信号判断,上升沿。
1 //2 module pulse_detect(3 output sig_b,4 input clka, //fast 300M5 input clkb, //slow 100M6 input rst_n,7 input sig_a);8 9 reg [2:0] sig_b_reg;10 reg sig_a_reg;11 12 always@(posedge clka or negedge rst_n) begin13 if(!rst_n)14 sig_a_reg <= 0;15 else if(sig_a)16 sig_a_reg <= ~sig_a_reg;17 end18 19 always@(posedge clkb or negedge rst_n) begin20 if(!rst_n)21 sig_b_reg <= 3'b000;22 else23 sig_b_reg <= {sig_b_reg[1:0],sig_a_reg};24 end25 26 assign sig_b = ^sig_b_reg[2:1];27 endmodule
tb:
1 //`timescale 100ps/100ps2 module tb;3 4 reg clka,clkb,rst_n,sig_a;5 wire sig_b;6 7 pulse_detect dut(.clka(clka),8 .clkb(clkb),9 .rst_n(rst_n),10 .sig_a(sig_a),11 .sig_b(sig_b));12 13 initial begin14 clka = 0;15 forever begin16 #1.6ns clka = ~clka;17 end18 end19 initial begin20 clkb = 0;21 forever begin22 #5ns clkb = ~clkb;23 end24 end25 26 initial begin27 rst_n = 0;28 sig_a = 0;29 30 #10ns31 rst_n = 1;32 sig_a = 1;33 #3.2ns34 sig_a = 0;35 #200ns36 $stop();37 end38 39 initial begin40 // $fsdbDumpfile("pulse_detect.fsdb");41 // $fsdbDumpvars;42 $vcdpluson;43 end44 45 46 47 48 endmodule : tb
~
~
波形
1.2 带有握手信号的脉冲同步
如果两个脉冲的间隔短,会丢失信号。需要使用握手信号确保。[refer]
module HANDSHAKE_PULSE_SYNC(src_clk , //source clock src_rst_n , //source clock reset (0: reset)src_pulse , //source clock pulse insrc_sync_fail , //source clock sync state: 1 clock pulse if sync fail.dst_clk , //destination clock dst_rst_n , //destination clock reset (0:reset)dst_pulse //destination pulse out);
//PARA DECLARATION
//INPUT DECLARATION
input src_clk ; //source clock
input src_rst_n ; //source clock reset (0: reset)
input src_pulse ; //source clock pulse in
input dst_clk ; //destination clock
input dst_rst_n ; //destination clock reset (0:reset)
//OUTPUT DECLARATION
output src_sync_fail ; //source clock sync state: 1 clock pulse if sync fail.
output dst_pulse ; //destination pulse out
//INTER DECLARATION
wire dst_pulse ;
wire src_sync_idle ;
reg src_sync_fail ;
reg src_sync_req ;
reg src_sync_ack ;
reg ack_state_dly1 ;
reg ack_state_dly2 ;
reg req_state_dly1 ;
reg req_state_dly2 ;
reg dst_req_state ;
reg dst_sync_ack ;
//--========================MODULE SOURCE CODE==========================--
//--=========================================--
// DST Clock :
// 1. generate src_sync_fail;
// 2. generate sync req
// 3. sync dst_sync_ack
//--=========================================--
assign src_sync_idle = ~(src_sync_req | src_sync_ack );
//report an error if src_pulse when sync busy ;
always @(posedge src_clk or negedge src_rst_n)
beginif(src_rst_n == 1'b0)src_sync_fail <= 1'b0 ;else if (src_pulse & (~src_sync_idle)) src_sync_fail <= 1'b1 ;elsesrc_sync_fail <= 1'b0 ;
end
//set sync req if src_pulse when sync idle ;
always @(posedge src_clk or negedge src_rst_n)
beginif(src_rst_n == 1'b0)src_sync_req <= 1'b0 ;else if (src_pulse & src_sync_idle) src_sync_req <= 1'b1 ;else if (src_sync_ack)src_sync_req <= 1'b0 ;
end
always @(posedge src_clk or negedge src_rst_n)
beginif(src_rst_n == 1'b0)beginack_state_dly1 <= 1'b0 ;ack_state_dly2 <= 1'b0 ;src_sync_ack <= 1'b0 ; endelsebeginack_state_dly1 <= dst_sync_ack ;ack_state_dly2 <= ack_state_dly1 ;src_sync_ack <= ack_state_dly2 ; end
end
//--=========================================--
// DST Clock :
// 1. sync src sync req
// 2. generate dst pulse
// 3. generate sync ack
//--=========================================--
always @(posedge dst_clk or negedge dst_rst_n)
beginif(dst_rst_n == 1'b0)beginreq_state_dly1 <= 1'b0 ;req_state_dly2 <= 1'b0 ;dst_req_state <= 1'b0 ;endelsebeginreq_state_dly1 <= src_sync_req ;req_state_dly2 <= req_state_dly1 ;dst_req_state <= req_state_dly2 ;end
end
//Rising Edge of dst_state generate a dst_pulse;
assign dst_pulse = (~dst_req_state) & req_state_dly2 ;
//set sync ack when src_req = 1 , clear it when src_req = 0 ;
always @(posedge dst_clk or negedge dst_rst_n)
beginif(dst_rst_n == 1'b0)dst_sync_ack <= 1'b0;else if (req_state_dly2) dst_sync_ack <= 1'b1;else dst_sync_ack <= 1'b0;
end
endmodule
2. 异步复位同步释放
使用异步复位同步释放来将输入数据a存储到寄存器中
module reset_release(
input clk,
input rst_n,
input d,
output reg dout);reg [1:0] rst_reg;//two level reg to syn rst_n=1always@(posedge clk or negedge rst_n) beginif(!rst_n)rst_reg <= 2'b0;elserst_reg <= {rst_reg[0],1'b1};endalways@(posedge clk or negedge rst_reg[1]) beginif(!rst_reg[1])dout <= 1'b0;elsedout <= d;endendmodule
module tb;
reg clk,rst_n,d;
wire dout;reset_release dut(.clk(clk),.rst_n(rst_n),.d(d),.dout(dout));initial beginclk = 0;forever begin#5clk = ~clk;endendinitial beginrst_n = 0;#8rst_n = 1;#200$stop();endinitial begind = 0;#10 d = 1;#10 d = 0;#15 d = 1;endendmodule : tb
3.握手信号跨时钟域
module data_driver(input clk_a,input rst_n,input data_ack,output reg [3:0]data,output reg data_req);reg [2:0] ack_reg;reg [2:0] count;//跨时钟域,此处打三拍避免亚稳态always@(posedge clk_a or negedge rst_n) beginif(!rst_n)ack_reg <= 3'b0;elseack_reg <= {ack_reg[1:0],data_ack};end//ack上升沿assign ack_posedge = !ack_reg[2] && ack_reg[1];always@(posedge clk_a or negedge rst_n) beginif(!rst_n)count <= 3'b0;else if(ack_posedge)count <= 3'b0;elsecount <= count + 1'b1;endalways@(posedge clk_a or negedge rst_n) beginif(!rst_n) begindata <= 3'b000;data_req <= 1'b0;endelse if(count == 3'b100)data_req <= 1'b1;else if(ack_posedge) begindata_req <= 1'b0;data <= data + 1'b1;endend
endmodulemodule data_receiver(input clk_b,input rst_n,input data_req,input [3:0] data,output reg data_ack);reg [3:0] data_reg;reg [2:0] req_reg;always@(posedge clk_b or negedge rst_n) beginif(!rst_n)req_reg <= 3'b0;elsereq_reg <= {req_reg[1:0],data_req};end//req上升沿assign req_posedge = !req_reg[2] && req_reg[1];always@(posedge clk_b or negedge rst_n) beginif(!rst_n) begindata_ack <= 1'b0;endelse if(req_reg[1]) begindata_ack <= 1'b1;elsedata_ack <= 1'b0;endendalways@(posedge clk_b or negedge rst_n) beginif(!rst_n) begindata_reg <= 4'b0;endelse if(req_posedge) begindata_reg <= data;endend
endmodule
tb:
reg clk_a,clk_b;
reg rst_n;data_driver dut_drv(.clk_a(clk_a),.rst_n(rst_n),.data_ack(data_ack),.data(data),.data_req(data_req));
data_receiver dut_rec(.clk_b(clk_b),.rst_n(rst_n),.data_ack(data_ack),.data(data),.data_req(data_req));initial beginclk_a = 0;forever begin#5ns clk_a = ~clk_a;end
endinitial beginclk_b = 0;forever begin#10ns clk_b = ~clk_b;end
endinitial beginrst_n = 0;#20nsrst_n = 1;#1us$stop();
end
endmodule : tb
4.小数分频
牛客
描述
请设计一个可以实现任意小数分频的时钟分频器,比如说8.7分频的时钟信号
注意rst为低电平复位
提示:
其实本质上是一个简单的数学问题,即如何使用最小公倍数得到时钟周期的分别频比。
设小数为nn,此处以8.7倍分频的时钟周期为例。
首先,由于不能在硬件上进行小数的运算(比如2.1个时钟这种是不现实的,也不存在3.3个寄存器),小数分频不能做到分频后每个时钟周期都是源时钟的nn倍,也无法实现占空比为1/2,因此,考虑小数分频,其实现方式应当为53个clkout时钟周期是10个clkin时钟周期的8.7倍。
信号示意图:
8.7分频,即周期扩大8.7倍。原来87个周期,现在10个周期。
则 x+y = 10
Mx+Ny = 87
题目所给M = 8 N = 9 ,所以可以由三个8分频的时钟和七个9分频的时钟组合而成。
设置一个div_flag,在计数27 87翻转,完成时钟的切换。
module div_M_N(input wire clk_in,input wire rst,output wire clk_out
);
parameter M_N = 8'd87;
parameter c89 = 8'd24; // 8/9时钟切换点
parameter div_e = 5'd8; //偶数周期
parameter div_o = 5'd9; //奇数周期
//*************code***********//
reg div_flag;
reg [6:0] clk_cnt;
reg [3:0] cnt;
reg clk_out_r;
always@(posedge clk_in or negedge rst) beginif(!rst) div_flag <= 'b0;else div_flag <= (clk_cnt== c89-1 || clk_cnt==M_N-1)? ~div_flag : div_flag;
end
always@(posedge clk_in or negedge rst) beginif(!rst)cnt <= 'b0;else if(!div_flag)cnt <= (cnt == div_e - 1? 0 : cnt + 1);else cnt <= (cnt == div_o - 1? 0 : cnt + 1);
endalways@(posedge clk_in or negedge rst) beginif(!rst)clk_cnt <= 'b0;else clk_cnt <= (clk_cnt == M_N-1) ? 'b0 : clk_cnt+1;
end
always@(posedge clk_in or negedge rst) beginif(!rst)clk_out_r <= 'b0;else if(!div_flag)clk_out_r <= cnt<=(div_e>>1-1);else clk_out_r <= cnt<=(div_o>>1-1);
end
assign clk_out = clk_out_r;
//*************code***********//
endmodule
5 状态机
5.1 售货机
牛客
描述
请设计状态机电路,实现自动售卖机功能,A饮料5元钱,B饮料10元钱,售卖机可接收投币5元钱和10元钱,每次投币只可买一种饮料,考虑找零的情况。
电路的接口如下图所示。sel信号会先于din信号有效,且在购买一种饮料时值不变。
sel为选择信号,用来选择购买饮料的种类,sel=0,表示购买A饮料,sel=1,表示购买B饮料;
din表示投币输入,din=0表示未投币,din=1表示投币5元,din=2表示投币10元,不会出现din=3的情况;
drinks_out表示饮料输出,drinks_out=0表示没有饮料输出,drinks_out=1表示输出A饮料,drinks_out=2表示输出B饮料,不出现drinks_out =3的情况,输出有效仅保持一个时钟周期;
change_out表示找零输出,change_out=0表示没有找零,change_out=1表示找零5元,输出有效仅保持一个时钟周期。
接口电路图如下:
使用Mealy型描述,所需要的状态数目会少很多,相对本题来说。
`timescale 1ns/1nsmodule sale(input clk ,input rst_n ,input sel ,//sel=0,5$dranks,sel=1,10&=$drinksinput [1:0] din ,//din=1,input 5$,din=2,input 10$output reg [1:0] drinks_out,//drinks_out=1,output 5$ drinks,drinks_out=2,output 10$ drinksoutput reg change_out
);
parameter IDLE = 'd0;
parameter S0 = 'd1;
reg n_state,c_state;
wire [2:0] inp;
assign inp = {sel,din};
always@(posedge clk or negedge rst_n) beginif(!rst_n)c_state <= IDLE;else c_state <= n_state;
end
always@(*)
case (c_state)IDLE : n_state = (inp=='b101) ? S0 : IDLE;S0 : n_state = (din=='b0) ? S0 : IDLE;default:n_state = IDLE;
endcase always@(posedge clk or negedge rst_n) beginif(!rst_n){drinks_out,change_out} <= 'b00;else beginif(c_state== S0) beginif(inp=='b101){drinks_out,change_out} <= 'b100;else if(inp == 'b110) {drinks_out,change_out} <= 'b101;else {drinks_out,change_out} <= 'b0;end else beginif(inp=='b001){drinks_out,change_out} <= 'b010;else if(inp == 'b010){drinks_out,change_out} <= 'b011;else if(inp == 'b110){drinks_out,change_out} <= 'b100;else {drinks_out,change_out} <= 'b0;endendendendmodule
更多推荐
Verilog经典题
发布评论