FPGA Prototyping By Verilog Examples第四章 常用时序电路设计
通用移位寄存器
通用移位寄存器可以载入并行数据,左移,右移,保持;它能够实现并-串功能(先载入并行数据后移位),也可实现串并功能(先移位后并行输出)。
// Listing 4.8
module univ_shift_reg
#(parameter N=8)
(
input wire clk, reset,
input wire [1:0] ctrl,
input wire [N-1:0] d,
output wire [N-1:0] q
);
//signal declaration
reg [N-1:0] r_reg, r_next;
// body
// register
always @(posedge clk, posedge reset)
if (reset)
r_reg <= 0;
else
r_reg <= r_next;
// next-state logic
always @*
case(ctrl)
2'b00: r_next = r_reg; // no op
2'b01: r_next = {r_reg[N-2:0], d[0]}; // shift left
2'b10: r_next = {d[N-1], r_reg[N-1:1]}; // shift right
default: r_next = d; // load
endcase
// output logic
assign q = r_reg;
endmodule
通用二进制计数器
module univ_bin_counter
#(parameter N=8)
(
input wire clk, reset,
input wire syn_clr, load, en, up,
input wire [N-1:0] d,
output wire max_tick, min_tick,
output wire [N-1:0] q
);
//signal declaration
reg [N-1:0] r_reg, r_next;
// body
// register
always @(posedge clk, posedge reset)
if (reset)
r_reg <= 0; //
else
r_reg <= r_next;
// next-state logic
always @*
if (syn_clr)
r_next = 0;
else if (load)
r_next = d;
else if (en & up)
r_next = r_reg + {{(N-1){1'b0}},1'b1};
else if (en & ~up)
r_next = r_reg - {{(N-1){1'b0}},1'b1};
else
r_next = r_reg;
// output logic
assign q = r_reg;
assign max_tick = (r_reg=={N{1'b1}}) ? 1'b1 : 1'b0;
assign min_tick = (r_reg=={N{1'b0}}) ? 1'b1 : 1'b0;
endmodule
测试文件
`timescale 1 ns/10 ps
// The `timescale directive specifies that
// the simulation time unit is 1 ns and
// the simulator timestep is 10 ps
module bin_counter_tb();
// declaration
localparam T=20; // clock period
reg clk, reset;
reg syn_clr, load, en, up;
reg [2:0] d;
wire max_tick, min_tick;
wire [2:0] q;
// uut instantiation
univ_bin_counter #(.N(3)) uut
(.clk(clk), .reset(reset), .syn_clr(syn_clr),
.load(load), .en(en), .up(up), .d(d),
.max_tick(max_tick), .min_tick(min_tick), .q(q));
// clock
// 20 ns clock running forever
always
begin
clk = 1'b1;
#(T/2);
clk = 1'b0;
#(T/2);
end
// reset for the first half cycle
initial
begin
reset = 1'b1;
#(T/2);
reset = 1'b0;
end
// other stimulus
initial
begin
// ==== initial input =====
syn_clr = 1'b0;
load = 1'b0;
en = 1'b0;
up = 1'b1; // count up
d = 3'b000;
@(negedge reset); // wait reset to deassert
@(negedge clk); // wait for one clock
// ==== test load =====
load = 1'b1;
d = 3'b011;
@(negedge clk); // wait for one clock
load = 1'b0;
repeat(2) @(negedge clk);
// ==== test syn_clear ====
syn_clr = 1'b1; // assert clear
@(negedge clk);
syn_clr = 1'b0;
// ==== test up counter and pause ====
en = 1'b1; // count
up = 1'b1;
repeat(10) @(negedge clk);
en = 1'b0; // pause
repeat(2) @(negedge clk);
en = 1'b1;
repeat(2) @(negedge clk);
// ==== test down counter ====
up = 1'b0;
repeat(10) @(negedge clk);
// ==== wait statement ====
// continue until q=2
wait(q==2);
@(negedge clk);
up = 1'b1;
// continue until min_tick becomes 1
@(negedge clk);
wait(min_tick);
@(negedge clk);
up = 1'b0;
// ==== absolute delay ====
#(4*T); // wait for 80 ns
en = 1'b0; // pause
#(4*T); // wait for 80 ns
// ==== stop simulation ====
// return to interactive simulation mode
$stop;
end
endmodule
通用模m计数器
相似命题讨论:
http://www.cnblogs.com/yuphone/archive/2010/12/26/1917395.html
module mod_m_counter
#(
parameter N=4, // number of bits in counter
M=10 // mod-M
)
(
input wire clk, reset,
output wire max_tick,
output wire [N-1:0] q
);
//signal declaration
reg [N-1:0] r_reg;
wire [N-1:0] r_next;
// body
// register
always @(posedge clk, posedge reset)
if (reset)
r_reg <= 0;
else
r_reg <= r_next;
// next-state logic
assign r_next = (r_reg==(M-1)) ? 0 : r_reg + 1;
// output logic
assign q = r_reg;
assign max_tick = (r_reg==(M-1)) ? 1'b1 : 1'b0;
endmodule
一种自动计算N的实现:
module mod_m_counter_fc
#(parameter M=10) // mod-M
(
input wire clk, reset,
output wire max_tick,
output wire [log2(M)-1:0] q
);
//signal declaration
localparam N = log2(M); // number of bits for M
reg [N-1:0] r_reg;
wire [N-1:0] r_next;
// body
// register
always @(posedge clk, posedge reset)
if (reset)
r_reg <= 0;
else
r_reg <= r_next;
// next-state logic
assign r_next = (r_reg==(M-1)) ? 0 : r_reg + 1;
// output logic
assign q = r_reg;
assign max_tick = (r_reg==(M-1)) ? 1'b1 : 1'b0;
// log2 constant function
function integer log2(input integer n);
integer i;
begin
log2 = 1;
for (i = 0; 2**i < n; i = i + 1)
log2 = i + 1;
end
endfunction
endmodule
基于循环队列的FIFO设计
http://www.cnblogs.com/yuphone/archive/2010/12/22/1913500.html
module fifo
#(
parameter B=8, // number of bits in a word
W=3 // number of address bits
)
(
// global clock and aysn reset
input clk,
input rst_n,
// fifo interface
// fifo control signnal
input rd,
input wr,
// fifo status signal
output empty,
output full,
// fifo data bus
input [B-1:0] w_data,
output reg [B-1:0] r_data,
output reg [W:0] cnt
);
// signal declaration
reg [B-1:0] array_reg [2**W-1:0]; // register array
reg [W-1:0] w_ptr_reg, w_ptr_next, w_ptr_succ;
reg [W-1:0] r_ptr_reg, r_ptr_next, r_ptr_succ;
reg full_reg, empty_reg, full_next, empty_next;
wire wr_en,rd_en;
// body
// register file write operation
// register file read operation
always @(posedge clk)
if (wr_en)
begin
array_reg[w_ptr_reg] <= w_data;
cnt <= cnt + 1'b1;
end
else if (rd_en)
begin
r_data <= array_reg[r_ptr_reg];
cnt <= cnt -1'b1;
end
// write enabled only when FIFO is not full
assign wr_en = wr & ~full_reg;
// write enabled only when FIFO is not empty
assign rd_en = rd & ~empty_reg;
// fifo control logic
// register for read and write pointers
always @(posedge clk, negedge rst_n)
if (!rst_n)
begin
cnt <= 0;
w_ptr_reg <= 0;
r_ptr_reg <= 0;
full_reg <= 1'b0;
empty_reg <= 1'b1;
end
else
begin
w_ptr_reg <= w_ptr_next;
r_ptr_reg <= r_ptr_next;
full_reg <= full_next;
empty_reg <= empty_next;
end
// next-state logic for read and write pointers
always @*
begin
// successive pointer values
w_ptr_succ = w_ptr_reg + 1;
r_ptr_succ = r_ptr_reg + 1;
// default: keep old values
w_ptr_next = w_ptr_reg;
r_ptr_next = r_ptr_reg;
full_next = full_reg;
empty_next = empty_reg;
case ({wr, rd})
// 2'b00: no op
2'b01: // read
if (~empty_reg) // not empty
begin
r_ptr_next = r_ptr_succ;
full_next = 1'b0;
if (r_ptr_succ==w_ptr_reg)
empty_next = 1'b1;
end
2'b10: // write
if (~full_reg) // not full
begin
w_ptr_next = w_ptr_succ;
empty_next = 1'b0;
if (w_ptr_succ==r_ptr_reg)
full_next = 1'b1;
end
2'b11: // write and read
begin
w_ptr_next = w_ptr_succ;
r_ptr_next = r_ptr_succ;
end
endcase
end
// output
assign full = full_reg;
assign empty = empty_reg;
endmodule
测试文件:
`timescale 1ns/1ns
module fifo_tb;
localparam T=20; // clock period
// global clock and asyn reset
reg clk, rst_n;
// fifo interface
reg rd, wr;
wire empty, full;
reg [7:0] w_data;
wire [7:0] r_data;
wire [3:0] cnt;
// fifo instantiation
fifo #(.B(8), .W(3)) fifo_inst
(
.clk(clk), .rst_n(rst_n),
.rd(rd), .wr(wr),
.empty(empty), .full(full),
.w_data(w_data), .r_data(r_data),
.cnt(cnt)
);
// clcok
always
begin
clk = 1'b0;
#(T/2);
clk = 1'b1;
#(T/2);
end
// reset
initial
begin
rst_n = 1'b0;
#(T/2)
rst_n = 1'b1;
end
// stimulus body
initial
begin
// initial input; empty
rd=0; wr=0; w_data=8'h00;
@(posedge rst_n); // wait to deassert rst_n
@(negedge clk); // wait for a clock
// 1 write
wr=1; w_data=8'h11;
@(negedge clk); // wait to assert wr
wr=0;
@(negedge clk); // wait to deassert wr
// 3 writes
wr=1;
repeat(3)
begin
w_data=w_data+8'h11;
@(negedge clk);
end
wr=0;
@(negedge clk);
// 1 read
rd=1;
@(negedge clk); // wait to assert rd
rd=0;
@(negedge clk) // wait to deassert rd
// 4 writes
wr=1;
repeat(4)
begin
w_data=w_data+8'h11;
@(negedge clk);
end
wr=0;
@(negedge clk);
// 1 write; full
wr=1; w_data=8'hAA;
@(negedge clk);
wr=0;
@(negedge clk);
// 2 reads
rd=1;
repeat(2) @(negedge clk);
rd=0;
@(negedge clk);
// 5 reads
rd=1;
repeat(5) @(negedge clk);
rd=0;
@(negedge clk);
// 1 read; empty
rd=1;
@(negedge clk);
rd=0;
@(negedge clk);
$stop;
end
endmodule
FIFO的另一种实现方式:
module fifo
#(
parameter stack_width=8,//位宽
stack_height=8,//深度,即数据总量
stack_ptr_width=4,//指针宽度
AE_level =2,
AF_level =6,
HF_level =4
)
(
output reg [stack_width-1:0] Data_out,
output stack_full,stack_almost_full,stack_half_full,
output stack_almost_empty,stack_empty,
input [stack_width-1:0] Data_in,
input wr,rd,
input clk,rst
);
reg [stack_ptr_width-1:0] rd_ptr,wr_ptr;//指针间读写间隔的地址
reg [stack_ptr_width:0] ptr_gap;
reg [stack_width-1:0] stack[stack_height-1:0];//存储器阵列
//堆栈状态信号
assign stack_full = (ptr_gap == stack_height);
assign stack_almost_full = (ptr_gap == AF_level);
assign stack_half_full = (ptr_gap == HF_level);
assign stack_almost_empty = (ptr_gap == AE_level);
assign stack_empty = (ptr_gap == 0);
always @ (posedge clk,negedge rst)
if(!rst)
begin
Data_out <= 0;
rd_ptr <= 0;
wr_ptr <= 0;
ptr_gap <= 0;
end
else if(wr && (!rd) && (!stack_full))
begin
stack[wr_ptr] <= Data_in;
wr_ptr <= wr_ptr + 1'b1;
ptr_gap <= ptr_gap + 1'b1;
end
else if((!wr) && (rd) && (!stack_empty))
begin
Data_out <= stack[rd_ptr];
rd_ptr <= rd_ptr + 1'b1;
ptr_gap <= ptr_gap - 1'b1;
end
else if((wr) && (rd) && (stack_empty))
begin
stack[wr_ptr] <= Data_in;
wr_ptr <= wr_ptr + 1'b1;
ptr_gap <= ptr_gap + 1'b1;
end
else if((wr) && (rd) && (stack_full))
begin
Data_out <= stack[rd_ptr];
rd_ptr <= rd_ptr + 1'b1;
ptr_gap <= ptr_gap - 1'b1;
end
else if((wr) && (rd) && (!stack_full) && (!stack_empty))
begin
Data_out <= stack[rd_ptr];
stack[wr_ptr] <= Data_in;
rd_ptr <= rd_ptr + 1'b1;
wr_ptr <= wr_ptr + 1'b1;
end
endmodule
测试文件:
`timescale 1ns/1ns
module fifo_tb;
localparam T=20; // clock period
// global clock and asyn reset
reg clk, rst_n;
// fifo interface
reg rd, wr;
wire empty, full;
reg [7:0] w_data;
wire [7:0] r_data;
wire stack_almost_full,stack_half_full,stack_almost_empty;
// fifo instantiation
fifo #(.stack_height(8), .stack_ptr_width(3)) fifo_inst
(
.clk(clk), .rst(rst_n),
.rd(rd), .wr(wr),
.stack_empty(empty), .stack_full(full),
.stack_almost_full(stack_almost_full),
.stack_half_full(stack_half_full),
.stack_almost_empty(stack_almost_empty),
.Data_in(w_data), .Data_out(r_data)
);
// clcok
always
begin
clk = 1'b0;
#(T/2);
clk = 1'b1;
#(T/2);
end
// reset
initial
begin
rst_n = 1'b0;
#(T/2)
rst_n = 1'b1;
end
// stimulus body
initial
begin
// initial input; empty
rd=0; wr=0; w_data=8'h00;
@(posedge rst_n); // wait to deassert rst_n
@(negedge clk); // wait for a clock
// 1 write
wr=1; w_data=8'h11;
@(negedge clk); // wait to assert wr
wr=0;
@(negedge clk); // wait to deassert wr
// 3 writes
wr=1;
repeat(3)
begin
w_data=w_data+8'h11;
@(negedge clk);
end
wr=0;
@(negedge clk);
// 1 read
rd=1;
@(negedge clk); // wait to assert rd
rd=0;
@(negedge clk) // wait to deassert rd
// 4 writes
wr=1;
repeat(4)
begin
w_data=w_data+8'h11;
@(negedge clk);
end
wr=0;
@(negedge clk);
// 1 write; full
wr=1; w_data=8'hAA;
@(negedge clk);
wr=0;
@(negedge clk);
// 2 reads
rd=1;
repeat(2) @(negedge clk);
rd=0;
@(negedge clk);
// 5 reads
rd=1;
repeat(5) @(negedge clk);
rd=0;
@(negedge clk);
// 1 read; empty
rd=1;
@(negedge clk);
rd=0;
@(negedge clk);
$stop;
end
endmodule
Stack的设计
module stack #( parameter B=8, // number of bits in a word W=3 // number of address bits ) ( // global clock and aysn reset input clk, input rst_n, // stack interface // stack control signnal input rd, input wr, // stack status signal output empty, output full, // stack data bus input [B-1:0] w_data, output wire [B-1:0] r_data, output reg [W:0] top, output wire wr_en,rd_en, output reg [W:0] rd_ptr ); // signal declaration reg [B-1:0] array_reg [2**W-1:0]; // register array wire full_reg, empty_reg; //reg rpt; assign r_data =(rd_en&clk)?array_reg[rd_ptr-1]:r_data; // body always @(posedge clk,negedge rst_n) if(!rst_n) top <= 4'd0; else begin case({wr_en,rd_en}) 2'b01: //read begin rd_ptr <= top; top <= top -1'b1; end 2'b10: //write begin top <= top + 1'b1; array_reg[top] <= w_data; end 2'b11: begin top <=top +1'b1; array_reg[top] <= w_data; rd_ptr <= top; top <= top - 1'b1; end default:; endcase end // write enabled only when FIFO is not full assign wr_en = wr & ~full_reg; // write enabled only when FIFO is not empty assign rd_en = rd & ~empty_reg; // stack control logic // register for read and write pointers assign full_reg = (top=={1'b1,{W{1'b0}}})? 1:0; assign empty_reg = (top=={1'b0,{W{1'b0}}})? 1:0; // output assign full = full_reg; assign empty = empty_reg; endmodule
测试文件
`timescale 1ns/1ns module stack_tb; localparam T=20; // clock period // global clock and asyn reset reg clk, rst_n; // fifo interface reg rd, wr; wire rd_en,wr_en; wire empty, full; reg [7:0] w_data; wire [7:0] r_data; wire [3:0] top; wire [3:0] rd_ptr; // stack instantiation stack #(.B(8), .W(3)) fifo_inst ( .clk(clk), .rst_n(rst_n), .rd(rd), .wr(wr), .empty(empty), .full(full), .w_data(w_data), .r_data(r_data), .top(top),.rd_en(rd_en),.wr_en(wr_en), .rd_ptr(rd_ptr) ); // clcok always begin clk = 1'b0; #(T/2); clk = 1'b1; #(T/2); end // reset initial begin rst_n = 1'b0; #(T/2) rst_n = 1'b1; end // stimulus body initial begin // initial input; empty rd=0; wr=0; w_data=8'h00; @(posedge rst_n); // wait to deassert rst_n @(negedge clk); // wait for a clock // 1 write wr=1; w_data=8'h11; @(negedge clk); // wait to assert wr wr=0; @(negedge clk); // wait to deassert wr // 3 writes wr=1; repeat(3) begin w_data=w_data+8'h11; @(negedge clk); end wr=0; @(negedge clk); // 1 read rd=1; @(negedge clk); // wait to assert rd //@(negedge clk); // wait to assert rd rd=0; @(negedge clk) // wait to deassert rd // 4 writes wr=1; repeat(4) begin w_data=w_data+8'h11; @(negedge clk); end wr=0; @(negedge clk); // 1 write; full wr=1; w_data=8'hAA; @(negedge clk); wr=0; @(negedge clk); // 2 reads rd=1; repeat(2) @(negedge clk); rd=0; @(negedge clk); // 5 reads rd=1; repeat(5) @(negedge clk); rd=0; @(negedge clk); // 1 read; empty rd=1; @(negedge clk); rd=0; @(negedge clk); $stop; end endmodule
代码改进如下,也可以实现同样的功能:
module stack #( parameter B=8, // number of bits in a word W=3 // number of address bits ) ( // global clock and aysn reset input clk, input rst_n, // stack interface // stack control signnal input rd, input wr, // stack status signal output empty, output full, // stack data bus input [B-1:0] w_data, output reg [B-1:0] r_data, output reg [W:0] top, output wire wr_en,rd_en ); // signal declaration reg [B-1:0] array_reg [2**W-1:0]; // register array wire full_reg, empty_reg; assign r_data =(rd_en&clk)?array_reg[top]:r_data; // body always @(posedge clk,negedge rst_n) if(!rst_n) top <= 4'd0; else begin case({wr_en,rd_en}) 2'b01: //read begin top <= top -1'b1; end 2'b10: //write begin array_reg[top] <= w_data; top <= top + 1'b1; end 2'b11: begin array_reg[top] <= w_data; end default:; endcase end // write enabled only when FIFO is not full assign wr_en = wr & ~full_reg; // write enabled only when FIFO is not empty assign rd_en = rd & ~empty_reg; // stack control logic // register for read and write pointers assign full_reg = (top=={1'b1,{W{1'b0}}})? 1:0; assign empty_reg = (top=={1'b0,{W{1'b0}}})? 1:0; // output assign full = full_reg; assign empty = empty_reg; endmodule
路漫漫其修远兮,吾将上下而求索