Verilog实现常见电路(三)
目录:
一、边沿检测
二、串并转换
三、分频器
四、异步复位同步释放
五、序列信号产生器
六、序列检测器
七、双端口RAM
八、同步FIFO
九、异步FIFO
常见电路系列连接:
CSDNhttps://mp.csdn.net/mp_blog/creation/editor/122940873CSDNhttps://mp.csdn.net/mp_blog/creation/editor/122991113七、双端口RAM
module dualram#(
parameter ADDR_WIDTH = 4,
DATA_WIDTH = 8,
DATA_DEPTH = 8
)(
input rd_clk,
input wr_clk,
input rst_n,
//port a
input wr_en,
input [3:0] wr_addr,
input [7:0] wr_data,
//port b
input rd_en,
input [3:0] rd_addr,
output reg [7:0] rd_data
);
reg [DATA_WIDTH-1:0] register [DATA_DEPTH-1:0];
integer i;
always@(posedge wr_clk or negedge rst_n)
if(~rst_n)begin
for(i=0;i<DATA_DEPTH;i=i+1)begin
register[i]<='h0;
end
end
else if(wr_en)
register[wr_addr]<=wr_data;
always@(posedge rd_clk or negedge rst_n)
if(~rst_n)
rd_data<='h0;
else if(rd_en)
rd_data<=register[rd_addr];
endmodule
仿真代码
`timescale 1ns / 1ps
module tb_dualram;
// dualram Parameters
parameter PERIOD_WR = 10;
parameter PERIOD_RD = 20;
parameter ADDR_WIDTH = 4;
// dualram Inputs
bit rd_clk ;
bit wr_clk ;
reg rst_n ;
reg wr_en ;
reg [3:0] wr_addr;
reg [7:0] wr_data;
reg rd_en ;
reg [3:0] rd_addr;
// dualram Outputs
wire [7:0] rd_data ;
reg [7:0] data;
initial
begin
forever #(PERIOD_WR/2) wr_clk=~wr_clk;
end
initial
begin
forever #(PERIOD_RD/2) rd_clk=~rd_clk;
end
initial
begin
rst_n = 0;
#(PERIOD_WR*2) rst_n = 1;
write_data(1,1);
write_data(3,2);
write_data(5,3);
read_data(1,data);
read_data(3,data);
read_data(5,data);
end
task write_data(input int addr,input reg [7:0] data);
begin
@(posedge wr_clk);
wr_en <= 1;
wr_addr <= addr;
wr_data <= data;
@(posedge wr_clk);
wr_en <= 0;
end
endtask
task read_data(input int addr,output reg [7:0] data);
begin
@(posedge rd_clk);
rd_en <= 1;
rd_addr <= addr;
data <= rd_data;
@(posedge rd_clk);
rd_en <= 0;
end
endtask
dualram #(
.ADDR_WIDTH ( ADDR_WIDTH ))
u_dualram (
.rd_clk ( rd_clk ),
.wr_clk ( wr_clk ),
.rst_n ( rst_n ),
.wr_en ( wr_en ),
.wr_addr ( wr_addr [3:0] ),
.wr_data ( wr_data [7:0] ),
.rd_en ( rd_en ),
.rd_addr ( rd_addr [3:0] ),
.rd_data ( rd_data [7:0] )
);
endmodule
波形图
八、同步FIFO
FIFO是由读写控制逻辑加RAM构成的,实现FIFO的关键因素就是读写指针、空满标志。
读写指针通过wr_ptr、rd_ptr两个计数器来实现,空满标志通过count计数器来判断,写入一个计数器加一,读出一个计数器减一,当count==0时表示FIFO空,当count==depth时表示fifo满
module fifo_out#(
parameter C_FIFO_DEPTH=128,
parameter C_FIFO_WIDTH=8
)(
input clk,
input rst_n,
input wr_vld,
output wr_rdy,
input [C_FIFO_WIDTH-1:0] data_i,
output rd_vld,
input rd_rdy,
output reg [C_FIFO_WIDTH-1:0] data_o
);
function integer clogb2(input integer depth);
begin
for(clogb2=0;depth>0;clogb2=clogb2+1)begin
depth=depth >> 1;
end
end
endfunction
reg [C_FIFO_WIDTH-1:0] fifo_data [C_FIFO_DEPTH-1:0];
reg [clogb2(C_FIFO_DEPTH-1)-1:0] wr_ptr,rd_ptr;
reg [clogb2(C_FIFO_DEPTH-1)-1:0] count;
wire full,empty;
wire wr_en,rd_en;
assign wr_en=wr_vld && wr_rdy;
assign rd_en=rd_vld && rd_rdy;
always@(posedge clk or negedge rst_n)
if(~rst_n)
wr_ptr<='h0;
else if(wr_ptr==C_FIFO_DEPTH-1)
wr_ptr<='h0;
else if(wr_en && ~full)begin
fifo_data[wr_ptr]<=data_i;
wr_ptr<= wr_ptr + 1'b1;
end
always@(posedge clk or negedge rst_n)
if(~rst_n)begin
rd_ptr<='h0;
data_o<='h0;
end
else if(rd_ptr==C_FIFO_DEPTH-1)
rd_ptr<='h0;
else if(rd_en && ~empty)begin
rd_ptr<=rd_ptr + 1'b1;
data_o<=fifo_data[rd_ptr];
end
always@(posedge clk or negedge rst_n)
if(~rst_n)
count<='h0;
else begin
case({wr_en,rd_en})
2'b00:count<=count;
2'b01:if(count!='h0)
count<=count-1'b1;
2'b10:if(count!=C_FIFO_DEPTH-1)
count<=count+1'b1;
2'b11:count<=count;
endcase
end
assign wr_rdy=wr_vld && ~full;
assign rd_vld=~empty;
assign empty = (count=='h0)? 1'b1:1'b0;
assign full = (count==C_FIFO_DEPTH-1)? 1'b1:1'b0;
endmodule
仿真代码
`timescale 1ns / 1ps
module tb_fifo_out;
// fifo_out Parameters
parameter PERIOD = 10;
parameter C_FIFO_DEPTH = 128;
// fifo_out Inputs
bit clk ;
reg rst_n ;
reg wr_vld ;
reg [7:0] data_i;
reg rd_rdy ;
// fifo_out Outputs
wire wr_rdy ;
wire rd_vld ;
wire [7:0] data_o;
initial
begin
forever #(PERIOD/2) clk=~clk;
end
initial
begin
rst_n=0;
rd_rdy=0;
#(PERIOD*2) rst_n = 1;
write_data(128);
#100;
rd_rdy=1;
end
integer i;
task write_data(input int unsigned number);
begin
for(i=0;i<number;i++)begin
@(posedge clk);
wr_vld<=1'b1;
data_i<=($random)%20;
end
wr_vld<=0;
data_i<=0;
end
endtask
fifo_out #(
.C_FIFO_DEPTH ( C_FIFO_DEPTH ))
u_fifo_out (
.clk ( clk ),
.rst_n ( rst_n ),
.wr_vld ( wr_vld ),
.data_i ( data_i [7:0] ),
.rd_rdy ( rd_rdy ),
.wr_rdy ( wr_rdy ),
.rd_vld ( rd_vld ),
.data_o ( data_o [7:0] )
);
endmodule
波形图
九、异步FIFO
异步FIFO因为读写不在一个时钟域,因此fifo的空满信号不能通过读写指针直接进行判断,空信号需要先将写指针转换为格雷码(因格雷码相邻数之间每次只有1bit发生变化)其次进行打两拍,最后将同步到读时钟域的写指针和读指针进行判断得到fifo的空状态;满状态同理将读指针同步到写时钟域进行判断。
写指针经过打拍后在同一时刻滞后于读指针,因此空是非空的,同理满是非满的,但不影响系统的正常工作
module fifo_async #(
parameter C_FIFO_WIDTH=8,
C_FIFO_DEPTH=32
)(
input rst_n,
input wr_clk,
input wr_valid,
output wr_ready,
input [C_FIFO_WIDTH-1:0] data_in,
input rd_clk,
output rd_valid,
input rd_ready,
output reg [C_FIFO_WIDTH-1:0] data_out,
output full,
output empty
);
integer i;
function integer clogb2(input integer depth);
begin
for(clogb2=0;depth>0;clogb2=clogb2+1'b1)begin
depth=depth>>1;
end
end
endfunction
reg [C_FIFO_WIDTH-1:0] fifo_data [C_FIFO_DEPTH-1:0];
reg [clogb2(C_FIFO_DEPTH-1):0] wr_ptr,rd_ptr;
wire [clogb2(C_FIFO_DEPTH-1)-1:0] wr_ptr_ture,rd_ptr_ture;
wire [clogb2(C_FIFO_DEPTH-1):0] wr_ptr_gray;
wire [clogb2(C_FIFO_DEPTH-1):0] rd_ptr_gray;
reg [clogb2(C_FIFO_DEPTH-1):0] wr_ptr_gray1,wr_ptr_gray2;
reg [clogb2(C_FIFO_DEPTH-1):0] rd_ptr_gray1,rd_ptr_gray2;
wire wr_en,rd_en;
assign wr_en = wr_valid && wr_ready;
assign rd_en = rd_valid && rd_ready;
assign wr_ptr_ture = wr_ptr[clogb2(C_FIFO_DEPTH-1)-1:0];
assign rd_ptr_ture = rd_ptr[clogb2(C_FIFO_DEPTH-1)-1:0];
assign wr_ready=wr_valid && ~full;
assign rd_valid=~empty;
always@(posedge wr_clk or negedge rst_n)
if(~rst_n)
wr_ptr<='h0;
else if(wr_en && ~full)begin
fifo_data[wr_ptr_ture]<=data_in;
wr_ptr<=wr_ptr+1'b1;
end
always@(posedge rd_clk or negedge rst_n)
if(~rst_n)begin
rd_ptr<='h0;
data_out<='h0;
end
else if(rd_en && ~empty)begin
data_out<=fifo_data[rd_ptr_ture];
rd_ptr<=rd_ptr+1'b1;
end
//binary2gray
assign wr_ptr_gray = (wr_ptr>>1) ^ wr_ptr;
assign rd_ptr_gray = (rd_ptr>>1) ^ rd_ptr;
always@(posedge rd_clk or negedge rst_n)
if(~rst_n)begin
wr_ptr_gray1<='h0;
wr_ptr_gray2<='h0;
end
else begin
wr_ptr_gray1<=wr_ptr_gray;
wr_ptr_gray2<=wr_ptr_gray1;
end
always@(posedge wr_clk or negedge rst_n)
if(~rst_n)begin
rd_ptr_gray1<='h0;
rd_ptr_gray2<='h0;
end
else begin
rd_ptr_gray1<=rd_ptr_gray;
rd_ptr_gray2<=rd_ptr_gray1;
end
assign empty = (wr_ptr_gray2==rd_ptr_gray)? 1'b1: 1'b0;
assign full = (wr_ptr_gray[7:0]=={~(rd_ptr_gray2[clogb2(C_FIFO_DEPTH-1)-:2]),rd_ptr_gray2[clogb2(C_FIFO_DEPTH-1)-2:0]})? 1'b1: 1'b0;
endmodule
仿真代码
`timescale 1ns / 1ps
module tb_fifo_async;
// fifo_async Parameters
parameter WR_PERIOD = 10;
parameter RD_PERIOD = 20;
parameter C_FIFO_WIDTH = 8;
parameter C_FIFO_DEPTH = 128;
// fifo_async Inputs
reg rst_n ;
bit wr_clk ;
bit rd_clk ;
reg wr_valid ;
reg [C_FIFO_WIDTH-1:0] data_in;
reg rd_ready ;
// fifo_async Outputs
wire wr_ready ;
wire rd_valid ;
wire [C_FIFO_WIDTH-1:0] data_out ;
wire full ;
wire empty ;
always #(WR_PERIOD/2) wr_clk=~wr_clk;
always #(RD_PERIOD/2) rd_clk=~rd_clk;
initial
begin
rst_n = 0;
wr_valid=1'b0;
rd_ready=1'b0;
#(WR_PERIOD*2) rst_n = 1;
#(WR_PERIOD*2);
generate_data(130);
#100;
rd_ready = 1'b1;
end
integer i;
task generate_data(input int unsigned number);
begin
for(i=0;i<number;i=i+1)begin
@(posedge wr_clk);
wr_valid<=1'b1;
data_in<=($random)%20;
end
wr_valid<=1'b0;
data_in<='h0;
end
endtask
fifo_async #(
.C_FIFO_WIDTH ( C_FIFO_WIDTH ),
.C_FIFO_DEPTH (C_FIFO_DEPTH )
)u_fifo_async (
.rst_n ( rst_n ),
.wr_clk ( wr_clk ),
.wr_valid ( wr_valid ),
.data_in ( data_in [C_FIFO_WIDTH-1:0] ),
.rd_clk ( rd_clk ),
.rd_ready ( rd_ready ),
.wr_ready ( wr_ready ),
.rd_valid ( rd_valid ),
.data_out ( data_out [C_FIFO_WIDTH-1:0] ),
.full ( full ),
.empty ( empty )
);
endmodule
波形图