FPGA Prototyping By Verilog Examples第六章 状态机FSMD设计
FSMD(带数据通道的有限状态机)是FSM和常规时序电路的结合。
基于RT methodology的消抖电路设计
本设计中主要的数据通道是一个用户自定制的21位递减计数器,其作用为:
1:可初始化为一个指定的值;
2:具有递减计数和暂停计数的功能;
3:当计数器计数为0的时候,输出一个状态信号。
module debounce_explicit
(
input wire clk, reset,
input wire sw,
output reg db_level, db_tick
);
// symbolic state declaration
localparam [1:0]
zero = 2'b00,
wait0 = 2'b01,
one = 2'b10,
wait1 = 2'b11;
// number of counter bits (2^N * 20ns = 40ms)
localparam N=21;
// signal declaration
reg [1:0] state_reg, state_next;
reg [N-1:0] q_reg;
wire [N-1:0] q_next;
wire q_zero;
reg q_load, q_dec;//q_load:load the initial value;q_dec:enable the counter
// body
// fsmd state & data registers
always @(posedge clk, posedge reset)
if (reset)
begin
state_reg <= zero;
q_reg <= 0;
end
else
begin
state_reg <= state_next;
q_reg <= q_next;
end
// FSMD data path (counter) next-state logic
assign q_next = (q_load) ? {N{1'b1}} : // load 1..1
(q_dec) ? q_reg - 1 : // decrement
q_reg;
// status signal
assign q_zero = (q_next==0);
// FSMD control path next-state logic
always @*
begin
state_next = state_reg; // default state: the same
q_load = 1'b0; // default output: 0
q_dec = 1'b0; // default output: 0
db_tick = 1'b0; // default output: 0
case (state_reg)
zero:
begin
db_level = 1'b0;
if (sw)
begin
state_next = wait1;
q_load = 1'b1;
end
end
wait1:
begin
db_level = 1'b0;
if (sw)
begin
q_dec = 1'b1;
if (q_zero)
begin
state_next = one;
db_tick = 1'b1;
end
end
else // sw==0
state_next = zero;
end
one:
begin
db_level = 1'b1;
if (~sw)
begin
state_next = wait0;
q_load = 1'b1;
end
end
wait0:
begin
db_level = 1'b1;
if (~sw)
begin
q_dec = 1'b1;
if (q_zero)
state_next = zero;
end
else // sw==1
state_next = one;
end
default: state_next = zero;
endcase
end
endmodule
另一种可替代的代码风格:
将RT(寄存器传输)的操作嵌入到FSM控制通路中,我们不需要明确的指定数据通路的元素,只需要在相应的FSM状态中列出RT操作即可。
// Listing 6.2
module debounce
(
input wire clk, reset,
input wire sw,
output reg db_level, db_tick
);
// symbolic state declaration
localparam [1:0]
zero = 2'b00,
wait0 = 2'b01,
one = 2'b10,
wait1 = 2'b11;
// number of counter bits (2^N * 20ns = 40ms)
localparam N=21;
// signal declaration
reg [N-1:0] q_reg, q_next;
reg [1:0] state_reg, state_next;
// body
// fsmd state & data registers
always @(posedge clk, posedge reset)
if (reset)
begin
state_reg <= zero;
q_reg <= 0;
end
else
begin
state_reg <= state_next;
q_reg <= q_next;
end
// next-state logic & data path functional units/routing
always @*
begin
state_next = state_reg; // default state: the same
q_next = q_reg; // default q: unchnaged
db_tick = 1'b0; // default output: 0
case (state_reg)
zero:
begin
db_level = 1'b0;
if (sw)
begin
state_next = wait1;
q_next = {N{1'b1}}; // load 1..1
end
end
wait1:
begin
db_level = 1'b0;
if (sw)
begin
q_next = q_reg - 1;
if (q_next==0)
begin
state_next = one;
db_tick = 1'b1;
end
end
else // sw==0
state_next = zero;
end
one:
begin
db_level = 1'b1;
if (~sw)
begin
state_next = wait0;
q_next = {N{1'b1}}; // load 1..1
end
end
wait0:
begin
db_level = 1'b1;
if (~sw)
begin
q_next = q_reg - 1;
if (q_next==0)
state_next = zero;
end
else // sw==1
state_next = one;
end
default: state_next = zero;
endcase
end
endmodule
第二种描述方式(隐数据通道)基本上跟ASMD描述顺序一样,我们仅仅是把ASMD图转换成了HDL语言。虽然这种方法简单而且描述性强,但是数据通道的综合主要依赖于软件,不容易控制。
为了代码的可读性、明晰化和高效性,我们常常把复杂的数据通道提取出来单独描述。
Fibonacci number circuit(数列)
// Listing 6.4 module fib ( input wire clk, reset, input wire start, input wire [4:0] i, output reg ready, done_tick, output wire [19:0] f ); // symbolic state declaration localparam [1:0] idle = 2'b00, op = 2'b01, done = 2'b10; // signal declaration reg [1:0] state_reg, state_next; reg [19:0] t0_reg, t0_next, t1_reg, t1_next; reg [4:0] n_reg, n_next; // body // FSMD state & data registers always @(posedge clk, posedge reset) if (reset) begin state_reg <= idle; t0_reg <= 0; t1_reg <= 0; n_reg <= 0; end else begin state_reg <= state_next; t0_reg <= t0_next; t1_reg <= t1_next; n_reg <= n_next; end // FSMD next-state logic always @* begin state_next = state_reg; ready = 1'b0; done_tick = 1'b0; t0_next = t0_reg; t1_next = t1_reg; n_next = n_reg; case (state_reg) idle: begin ready = 1'b1; if (start) begin t0_next = 0; t1_next = 20'd1; n_next = i; state_next = op; end end op: if (n_reg==0) begin t1_next = 0; state_next = done; end else if (n_reg==1) state_next = done; else begin t1_next = t1_reg + t0_reg; t0_next = t1_reg; n_next = n_reg - 1; end done: begin done_tick = 1'b1; state_next = idle; end default: state_next = idle; endcase end // output assign f = t1_reg; endmodule
除法器
// Listing 6.5 module div #( parameter W = 8, CBIT = 4 // CBIT=log2(W)+1 ) ( input wire clk, reset, input wire start, input wire [W-1:0] dvsr, dvnd, output reg ready, done_tick, output wire [W-1:0] quo, rmd ); // symbolic state declaration localparam [1:0] idle = 2'b00, op = 2'b01, last = 2'b10, done = 2'b11; // signal declaration reg [1:0] state_reg, state_next; reg [W-1:0] rh_reg, rh_next, rl_reg, rl_next, rh_tmp; reg [W-1:0] d_reg, d_next; reg [CBIT-1:0] n_reg, n_next; reg q_bit; // body // FSMD state & data registers always @(posedge clk, posedge reset) if (reset) begin state_reg <= idle; rh_reg <= 0; rl_reg <= 0; d_reg <= 0; n_reg <= 0; end else begin state_reg <= state_next; rh_reg <= rh_next; rl_reg <= rl_next; d_reg <= d_next; n_reg <= n_next; end // FSMD next-state logic always @* begin state_next = state_reg; ready = 1'b0; done_tick = 1'b0; rh_next = rh_reg; rl_next = rl_reg; d_next = d_reg; n_next = n_reg; case (state_reg) idle: begin ready = 1'b1; if (start) begin rh_next = 0; rl_next = dvnd; // dividend d_next = dvsr; // divisor n_next = CBIT; // index state_next = op; end end op: begin // shift rh and rl left rl_next = {rl_reg[W-2:0], q_bit}; rh_next = {rh_tmp[W-2:0], rl_reg[W-1]}; // decrease index n_next = n_reg - 1; if (n_next==1) state_next = last; end last: // last iteration begin rl_next = {rl_reg[W-2:0], q_bit}; rh_next = rh_tmp; state_next = done; end done: begin done_tick = 1'b1; state_next = idle; end default: state_next = idle; endcase end // compare and subtract circuit always @* if (rh_reg >= d_reg) begin rh_tmp = rh_reg - d_reg; q_bit = 1'b1; end else begin rh_tmp = rh_reg; q_bit = 1'b0; end //output assign quo = rl_reg; assign rmd = rh_reg; endmodule
周期测量器
// Listing 6.7
module period_counter
(
input wire clk, reset,
input wire start, si,
output reg ready, done_tick,
output wire [9:0] prd
);
// symbolic state declaration
localparam [1:0]
idle = 2'b00,
waite = 2'b01,
count = 2'b10,
done = 2'b11;
// constant declaration
localparam CLK_MS_COUNT= 50000; // 1 ms tick
// signal declaration
reg [1:0] state_reg, state_next;
reg [15:0] t_reg, t_next; // up to 50000
reg [9:0] p_reg, p_next; // up to 1 sec
reg delay_reg;
wire edg;
// body
// FSMD state & data registers
always @(posedge clk, posedge reset)
if (reset)
begin
state_reg <= idle;
t_reg <= 0;
p_reg <= 0;
delay_reg <= 0;
end
else
begin
state_reg <= state_next;
t_reg <= t_next;
p_reg <= p_next;
delay_reg <= si;
end
// rising-edge tick
assign edg = ~delay_reg & si;
// FSMD next-state logic
always @*
begin
state_next = state_reg;
ready = 1'b0;
done_tick = 1'b0;
p_next = p_reg;
t_next = t_reg;
case (state_reg)
idle:
begin
ready = 1'b1;
if (start)
state_next = waite;
end
waite: // wait for the first edge
if (edg)
begin
state_next = count;
t_next = 0;
p_next = 0;
end
count:
if (edg) // 2nd edge arrived
state_next = done;
else // otherwise count
if (t_reg == CLK_MS_COUNT-1) // 1 ms tick
begin
t_next = 0;
p_next = p_reg + 1;
end
else
t_next = t_reg + 1;
done:
begin
done_tick = 1'b1;
state_next = idle;
end
default: state_next = idle;
endcase
end
//ouput
assign prd = p_reg;
endmodule
数字频率计
// Listing 6.8
module low_freq_counter
(
input wire clk, reset,
input wire start, si,
output wire [3:0] bcd3, bcd2, bcd1, bcd0
);
// symbolic state declaration
localparam [1:0]
idle = 2'b00,
count = 2'b01,
frq = 2'b10,
b2b = 2'b11;
// signal declaration
reg [1:0] state_reg, state_next;
wire [9:0] prd;
wire [19:0] dvsr, dvnd, quo;
reg prd_start, div_start, b2b_start;
wire prd_done_tick, div_done_tick, b2b_done_tick;
//===============================================
// component instantiation
//===============================================
// instantiate period counter
period_counter prd_count_unit
(.clk(clk), .reset(reset), .start(prd_start), .si(si),
.ready(), .done_tick(prd_done_tick), .prd(prd));
// instantiate division circuit
div #(.W(20), .CBIT(5)) div_unit
(.clk(clk), .reset(reset), .start(div_start),
.dvsr(dvsr), .dvnd(dvnd), .quo(quo), .rmd(),
.ready(), .done_tick(div_done_tick));
// instantiate binary-to-BCD convertor
bin2bcd b2b_unit
(.clk(clk), .reset(reset), .start(b2b_start),
.bin(quo[12:0]), .ready(), .done_tick(b2b_done_tick),
.bcd3(bcd3), .bcd2(bcd2), .bcd1(bcd1), .bcd0(bcd0));
// signal width extension
assign dvnd = 20'd1000000;
assign dvsr = {10'b0, prd};
//===============================================
// master FSM
//===============================================
always @(posedge clk, posedge reset)
if (reset)
state_reg <= idle;
else
state_reg <= state_next;
always @*
begin
state_next = state_reg;
prd_start = 1'b0;
div_start = 1'b0;
b2b_start = 1'b0;
case (state_reg)
idle:
if (start)
begin
prd_start = 1'b1;
state_next = count;
end
count:
if (prd_done_tick)
begin
div_start = 1'b1;
state_next = frq;
end
frq:
if (div_done_tick)
begin
b2b_start = 1'b1;
state_next = b2b;
end
b2b:
if (b2b_done_tick)
state_next = idle;
endcase
end
endmodule
二进制---BCD码转换
// Listing 6.6
module bin2bcd
(
input wire clk, reset,
input wire start,
input wire [12:0] bin,
output reg ready, done_tick,
output wire [3:0] bcd3, bcd2, bcd1, bcd0
);
// symbolic state declaration
localparam [1:0]
idle = 2'b00,
op = 2'b01,
done = 2'b10;
// signal declaration
reg [1:0] state_reg, state_next;
reg [12:0] p2s_reg, p2s_next;
reg [3:0] n_reg, n_next;
reg [3:0] bcd3_reg, bcd2_reg, bcd1_reg, bcd0_reg;
reg [3:0] bcd3_next, bcd2_next, bcd1_next, bcd0_next;
wire [3:0] bcd3_tmp, bcd2_tmp, bcd1_tmp, bcd0_tmp;
// body
// FSMD state & data registers
always @(posedge clk, posedge reset)
if (reset)
begin
state_reg <= idle;
p2s_reg <= 0;
n_reg <= 0;
bcd3_reg <= 0;
bcd2_reg <= 0;
bcd1_reg <= 0;
bcd0_reg <= 0;
end
else
begin
state_reg <= state_next;
p2s_reg <= p2s_next;
n_reg <= n_next;
bcd3_reg <= bcd3_next;
bcd2_reg <= bcd2_next;
bcd1_reg <= bcd1_next;
bcd0_reg <= bcd0_next;
end
// FSMD next-state logic
always @*
begin
state_next = state_reg;
ready = 1'b0;
done_tick = 1'b0;
p2s_next = p2s_reg;
bcd0_next = bcd0_reg;
bcd1_next = bcd1_reg;
bcd2_next = bcd2_reg;
bcd3_next = bcd3_reg;
n_next = n_reg;
case (state_reg)
idle:
begin
ready = 1'b1;
if (start)
begin
state_next = op;
bcd3_next = 0;
bcd2_next = 0;
bcd1_next = 0;
bcd0_next = 0;
n_next = 4'b1101; // index
p2s_next = bin; // shift register
state_next = op;
end
end
op:
begin
// shift in binary bit
p2s_next = p2s_reg << 1;
// shift 4 BCD digits
//{bcd3_next, bcd2_next, bcd1_next, bcd0_next}=
//{bcd3_tmp[2:0], bcd2_tmp, bcd1_tmp, bcd0_tmp,
// p2s_reg[12]}
bcd0_next = {bcd0_tmp[2:0], p2s_reg[12]};
bcd1_next = {bcd1_tmp[2:0], bcd0_tmp[3]};
bcd2_next = {bcd2_tmp[2:0], bcd1_tmp[3]};
bcd3_next = {bcd3_tmp[2:0], bcd2_tmp[3]};
n_next = n_reg - 1;
if (n_next==0)
state_next = done;
end
done:
begin
done_tick = 1'b1;
state_next = idle;
end
default: state_next = idle;
endcase
end
// data path function units
assign bcd0_tmp = (bcd0_reg > 4) ? bcd0_reg+3 : bcd0_reg;
assign bcd1_tmp = (bcd1_reg > 4) ? bcd1_reg+3 : bcd1_reg;
assign bcd2_tmp = (bcd2_reg > 4) ? bcd2_reg+3 : bcd2_reg;
assign bcd3_tmp = (bcd3_reg > 4) ? bcd3_reg+3 : bcd3_reg;
// output
assign bcd0 = bcd0_reg;
assign bcd1 = bcd1_reg;
assign bcd2 = bcd2_reg;
assign bcd3 = bcd3_reg;
endmodule
路漫漫其修远兮,吾将上下而求索