1.简易式同步fifo,3个always块搞定
`timescale 1ns/1ns
module sfifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input clk ,
input rst_n ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output reg wfull ,
output reg rempty ,
output wire [WIDTH-1:0] rdata
);
// 用localparam定义一个参数,可以在文件内使用
localparam ADDR_WIDTH = $clog2(DEPTH);
reg [ADDR_WIDTH:0] waddr;
reg [ADDR_WIDTH:0] raddr;
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
always @ (posedge clk or negedge rst_n) begin
if(~rst_n) begin
raddr <= 'b0;
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 <= 'b0;
rempty <= 'b0;
end
else begin
wfull <= (raddr == {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH-1:0]});
rempty <= (raddr == waddr);
end
end
// 带有 parameter 参数的例化格式
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
2.异步fifo,Verilog实现,注意空满逻辑判断,格雷码转换公式,深度计算
二进制转格雷码、格雷码转二进制 见下面的图片
空满逻辑判断
满:在写时钟域进行判断,如果wptr和rptr_cdc的高两位相反,其他位相同,则满
空:在读时钟域进行判断,如果rptr和wptr_cdc完全相同,则空
异步fifo会出现假空假满现象
异步fifo深度计算
如何简单快速地计算FIFO的最小深度?(笔面试常客)_孤独的单刀的博客-CSDN博客_fifo最小深度
`timescale 1ns/1ns
/********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
/********AFIFO*********/
module asyn_fifo#(
parameter WIDTH = 8,
parameter DEPTH = 16
)(
input wclk ,
input rclk ,
input wrstn ,
input rrstn ,
input winc ,
input rinc ,
input [WIDTH-1:0] wdata ,
output wire wfull ,
output wire rempty ,
output wire [WIDTH-1:0] rdata
);
localparam ADDR_WIDTH = $clog2(DEPTH);
reg [ADDR_WIDTH:0] waddr;
reg [ADDR_WIDTH:0] raddr;
always @ (posedge wclk or negedge wrstn) begin
if(~wrstn) begin
waddr <= 'b0;
end
else begin
if( winc && ~wfull ) begin
waddr <= waddr + 1'b1;
end
else begin
waddr <= waddr;
end
end
end
always @ (posedge rclk or negedge rrstn) begin
if(~rrstn) begin
raddr <= 'b0;
end
else begin
if( rinc && ~rempty ) begin
raddr <= raddr + 1'b1;
end
else begin
raddr <= raddr;
end
end
end
wire [ADDR_WIDTH:0] waddr_gray;
wire [ADDR_WIDTH:0] raddr_gray;
assign waddr_gray = waddr ^ (waddr>>1);
assign raddr_gray = raddr ^ (raddr>>1);
reg [ADDR_WIDTH:0] waddr_gray_reg;
always @ (posedge wclk or negedge wrstn) begin
if(~wrstn) begin
waddr_gray_reg <= 'd0;
end
else begin
waddr_gray_reg <= waddr_gray;
end
end
reg [ADDR_WIDTH:0] raddr_gray_reg;
always @ (posedge rclk or negedge rrstn) begin
if(~rrstn) begin
raddr_gray_reg <= 'd0;
end
else begin
raddr_gray_reg <= raddr_gray;
end
end
reg [ADDR_WIDTH:0] addr_r2w_t;
reg [ADDR_WIDTH:0] addr_r2w;
always @ (posedge wclk or negedge wrstn) begin
if(~wrstn) begin
addr_r2w_t <= 'd0;
addr_r2w <= 'd0;
end
else begin
addr_r2w_t <= raddr_gray_reg;
addr_r2w <= addr_r2w_t;
end
end
reg [ADDR_WIDTH:0] addr_w2r_t;
reg [ADDR_WIDTH:0] addr_w2r;
always @ (posedge rclk or negedge rrstn) begin
if(~rrstn) begin
addr_w2r_t <= 'd0;
addr_w2r <= 'd0;
end
else begin
addr_w2r_t <= waddr_gray_reg;
addr_w2r <= addr_w2r_t;
end
end
assign wfull = (waddr_gray_reg == {~addr_r2w[ADDR_WIDTH:ADDR_WIDTH-1], addr_r2w[ADDR_WIDTH-2:0]});
assign rempty = (raddr_gray_reg == addr_w2r);
dual_port_RAM
#(
.DEPTH(DEPTH),
.WIDTH(WIDTH)
)
dual_port_RAM_U0
(
.wclk(wclk),
.wenc(winc&&~wfull),
.waddr(waddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。
.wdata(wdata), //数据写入
.rclk(rclk),
.renc(rinc&&~rempty),
.raddr(raddr[ADDR_WIDTH-1:0]), //深度对2取对数,得到地址的位宽。
.rdata(rdata) //数据输出
);
endmodule
3.N倍时钟倍频器,下面实现了时钟的4倍频
`timescale 1ns/1ns
module Mul_clk_tb();
reg clk;
reg [3:0] clk_tmp;
wire clk1;
//CYCLE 和 NUM 需满足倍数关系
parameter NUM = 4;
parameter CYCLE = 20;
initial begin
clk = 1;
forever begin
#CYCLE clk = ~clk;
end
end
genvar i;
generate
for(i=0;i
4.分频器
包括奇数分频和偶数分频,还包括其他特殊的特殊的分频以及改变占空比
分频器 偶分频 奇分频 任意分频___Wang____的博客-CSDN博客
5.边沿检测器
包括上升沿检测和下降沿检测,以及两种边沿同时都检测
边沿检测 Verilog___Wang____的博客-CSDN博客_verilog边沿检测
6.序列检测器 可以使用状态机和移位寄存器两种方法
//状态机法
`timescale 1ns/1ns
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
parameter ZERO=0, ONE=1, TWO=2, THREE=3, FOUR=4, FIVE=5, SIX=6, SEVEN=7, EIGHT=8;
reg [3:0] state, nstate;
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
state <= ZERO;
else
state <= nstate;
end
always@(*) begin
case(state)
ZERO : nstate = a? ZERO : ONE;
ONE : nstate = a? TWO : ONE;
TWO : nstate = a? THREE: ONE;
THREE : nstate = a? FOUR : ONE;
FOUR : nstate = a? ZERO : FIVE;
FIVE : nstate = a? TWO : SIX;
SIX : nstate = a? TWO : SEVEN;
SEVEN : nstate = a? EIGHT: ONE;
EIGHT : nstate = a? THREE: ONE ;
default: nstate = ZERO;
endcase
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n)
match <= 0;
else
match <= state==EIGHT;
end
endmodule
//移位寄存器法
module sequence_detect(
input clk,
input rst_n,
input a,
output reg match
);
reg[7:0] a_r;
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
a_r<='b0;
end
else begin
a_r<={a_r[6:0],a};
end
end
always@(posedge clk or negedge rst_n) begin
if(~rst_n) begin
match <= 1'b0;
end
else begin
match <= a_r==8'b01110001;
end
end
endmodule
7.贩卖机
首先要将问题抽象出来,d1,d2,d3分别是投0.5,1,2三种硬币,out1,out2分别是是否输出饮料和找零多少钱,将d1,d2,d3做成一个数组,减少输入的判断语句,out2两位变量,2‘b0代表找零0,2'b1代表找零0.5,2‘b10代表找零1,2‘b11代表找零1.5元
第二个always块判断的是cur_state,并且对于cur_state的很多状态都直接让next_state为s0;
第三个always块判断的是next_state
`timescale 1ns/1ns
module seller1(
input wire clk ,
input wire rst ,
input wire d1 ,
input wire d2 ,
input wire d3 ,
output reg out1,
output reg [1:0]out2
);
parameter S0 = 'd0, S1 = 'd1, S2 = 'd2, S3 = 'd3 , S4 = 'd4, S5 = 'd5 , S6 = 'd6;
reg [2:0] current_state;
reg [2:0] next_state;
wire [2:0] input_state;//将输入组合起来
assign input_state = {d1,d2,d3};
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
current_state <= S0;
end
else begin
current_state <= next_state;
end
end
always@(*)begin
case(current_state)
S0:begin
case(input_state)
3'b100:next_state = S1 ;
3'b010:next_state = S2 ;
3'b001:next_state = S4 ;
default:next_state = next_state;
endcase
end
S1:begin
case(input_state)
3'b100:next_state = S2 ;
3'b010:next_state = S3 ;
3'b001:next_state = S5 ;
default:next_state = next_state;
endcase
end
S2:begin
case(input_state)
3'b100:next_state = S3 ;
3'b010:next_state = S4 ;
3'b001:next_state = S6 ;
default:next_state = next_state;
endcase
end
default:begin
next_state = S0;
end
endcase
end
always@(posedge clk or negedge rst)begin
if(rst == 1'b0)begin
out1 <= 1'b0;
out2 <= 2'b0;
end
else begin
case(next_state)
S3: begin out1 <= 1'b1;out2 <= 2'b0; end
S4: begin out1 <= 1'b1;out2 <= 2'b1; end
S5: begin out1 <= 1'b1;out2 <= 2'b10; end
S6: begin out1 <= 1'b1;out2 <= 2'b11; end
default: begin out1 <= 1'b0;out2 <= 2'b0; end
endcase
end
end
endmodule