FSM设计之二-----并行数据流转特殊串行数据流
夏宇闻《Verilog 数字系统设计教程》P216
设计两个可综合电路模块:M1模块能把4位的并行数据转换为符合以下协议的串行数据流,数据流用scl和sda两条线传输,sclk为输入的时钟信号,data[3:0]为输入数据,ack为M1的请求M0发新数据信号。第二个模块M2能把串行数据流内的信息接收到,并转换为相应16条信号线的高电平,即若数据为1,则第一条线路为高电平,数据为n,则第n条数据线为高电平。M0为 测试用的信号发生模块,该模块接收M1发出的ack信号,并产生新的测试数据data[3:0]。
通信协议:scl为不断输出的时钟信号,如果scl为高电平时,sda由高变低时刻,串行数据流开始;如果scl为高电平时,sda由低变高时刻,串行数据流结束。sda信号的串行数据位必须在scl为低电平时变化,若变为高则为1,否则为0。
M1模块的代码如下
1 `timescale 1ns/1ns
2 module ptosda
3 (
4 input sclk,
5 input rst_n,
6 input [3:0] data,
7 output reg ack,
8 output reg scl,
9 output sda
10 );
11
12 reg link_sda;
13 reg sdabuf;
14 reg [3:0] databuf;
15 reg [7:0] state;
16
17 parameter ready = 8'b0000_0001,
18 start = 8'b0000_0010,
19 bit1 = 8'b0000_0100,
20 bit2 = 8'b0000_1000,
21 bit3 = 8'b0001_0000,
22 bit4 = 8'b0010_0000,
23 bit5 = 8'b0100_0000,
24 stop = 8'b1000_0000,
25 idle = 8'b0000_0000;
26
27 assign sda = link_sda?sdabuf:1'bz;
28
29 always @ (posedge sclk,negedge rst_n)
30 begin
31 if(!rst_n) scl <= 1'b1;
32 else scl <= ~scl;
33 end
34
35 always @ (posedge ack)
36 begin
37 databuf <= data;
38 end
39
40 always @ (negedge sclk or negedge rst_n)
41 begin
42 if(!rst_n)
43 begin
44 link_sda<=1'b0;
45 state <= ready;
46 sdabuf<=1'b1;
47 ack<=1'b0;
48 end
49 else
50 begin
51 case(state)
52 ready:
53 if(ack)
54 begin
55 link_sda<=1'b1;
56 state <= start;
57 end
58 else
59 begin
60 link_sda<=1'b0;
61 state<=ready;
62 ack<=1'b1;
63 end
64 start:
65 if(scl && ack)
66 begin
67 sdabuf<=1'b0;
68 state<=bit1;
69 end
70 else state<= start;
71 bit1:
72 if(!scl)
73 begin
74 sdabuf<=databuf[3];
75 state<=bit2;
76 ack<=1'b0;
77 end
78 else state<=bit1;
79 bit2:
80 if(!scl)
81 begin
82 sdabuf<=databuf[2];
83 state<=bit3;
84 end
85 else state<=bit2;
86 bit3:
87 if(!scl)
88 begin
89 sdabuf<=databuf[1];
90 state<=bit4;
91 end
92 else state<=bit3;
93 bit4:
94 if(!scl)
95 begin
96 sdabuf<=databuf[0];
97 state<=bit5;
98 end
99 else state<=bit4;
100 bit5:
101 if(!scl)
102 begin
103 sdabuf<=1'b0;
104 state<=stop;
105 end
106 else state<=bit5;
107 stop:
108 if(scl)
109 begin
110 sdabuf<=1'b1;
111 state<=idle;
112 end
113 else state<=stop;
114 idle:
115 begin
116 link_sda<=1'b0;
117 state<=ready;
118 end
119 default:
120 begin
121 link_sda<=1'b0;
122 sdabuf<=1'b1;
123 state<=ready;
124 end
125 endcase
126 end
127 end
128
129 endmodule
M2模块的代码如下
1 `timescale 1ns/1ns
2 module out16hi
3 (
4 input scl,
5 input sda,
6 output reg [15:0] outhigh,
7 output reg [3:0] pdata,
8 output reg [3:0] pdatabuf,
9 output reg start_flag,
10 output reg end_flag,
11 output reg [5:0] mstate
12 );
13
14 //reg [5:0] mstate;/* synthesis preserve*/
15 //reg [3:0] pdatabuf;
16 //reg start_flag,end_flag;
17
18 always @ (negedge sda)
19 begin
20 if(scl)
21 start_flag<=1'b1;
22 else if(end_flag)
23 start_flag<=1'b0;
24 end
25
26 always @ (posedge sda)
27 begin
28 if(scl)
29 begin
30 end_flag<=1'b1;
31 pdatabuf<=pdata;
32 end
33 else
34 end_flag<=1'b0;
35 end
36
37 parameter ready = 6'b00_0000,
38 bit0 = 6'b00_0001,
39 bit1 = 6'b00_0010,
40 bit2 = 6'b00_0011,
41 bit3 = 6'b00_0100,
42 bit4 = 6'b00_0101;
43
44 always @ (pdatabuf)
45 begin
46 case(pdatabuf)
47 4'b0001 : outhigh = 16'b0000_0000_0000_0001;
48 4'b0010 : outhigh = 16'b0000_0000_0000_0010;
49 4'b0011 : outhigh = 16'b0000_0000_0000_0100;
50 4'b0100 : outhigh = 16'b0000_0000_0000_1000;
51 4'b0101 : outhigh = 16'b0000_0000_0001_0000;
52 4'b0110 : outhigh = 16'b0000_0000_0010_0000;
53 4'b0111 : outhigh = 16'b0000_0000_0100_0000;
54 4'b1000 : outhigh = 16'b0000_0000_1000_0000;
55 4'b1001 : outhigh = 16'b0000_0001_0000_0000;
56 4'b1010 : outhigh = 16'b0000_0010_0000_0000;
57 4'b1011 : outhigh = 16'b0000_0100_0000_0000;
58 4'b1100 : outhigh = 16'b0000_1000_0000_0000;
59 4'b1101 : outhigh = 16'b0001_0000_0000_0000;
60 4'b1110 : outhigh = 16'b0010_0000_0000_0000;
61 4'b1111 : outhigh = 16'b0100_0000_0000_0000;
62 4'b0000 : outhigh = 16'b1000_0000_0000_0000;
63 endcase
64 end
65
66 always @ (posedge scl)
67 begin
68 if(start_flag)
69 case(mstate)
70 bit0:
71 begin
72 mstate <= bit1;
73 pdata[3] <= sda;
74 $display("I am is sda bit0");
75 end
76 bit1:
77 begin
78 mstate <= bit2;
79 pdata[2] <= sda;
80 $display("I am is sda bit1");
81 end
82 bit2:
83 begin
84 mstate <= bit3;
85 pdata[1] <= sda;
86 $display("I am is sda bit2");
87 end
88 bit3:
89 begin
90 mstate <= bit4;
91 pdata[0] <= sda;
92 $display("I am is sda bit3");
93 end
94 bit4:
95 begin
96 mstate <= bit0;
97 $display("I am is sda stop");
98 end
99 default:
100 mstate <= bit0;
101 endcase
102 else
103 mstate <= bit0;
104 end
105
106 endmodule
信号测试模块:
`timescale 1ns/1ns `define half 50 module sigdata ( input ask_for_data, output reg rst_n, output reg sclk, output reg [3:0] data ); //parameter half=50; initial begin rst_n=1; #10 rst_n=0; #(`half*2+3) rst_n=1; end initial begin sclk=0; data=0; #(`half*1000) $stop; end always #(`half) sclk =~sclk; always @ (posedge ask_for_data) begin #(`half/2+3) data=data+1'b1; end endmodule
顶层模块:
1 `timescale 1ns/1ns
2 module top;
3 wire [3:0] data;
4 wire sclk,scl,sda;
5 wire rst_n;
6 wire [15:0] outhigh;
7 wire [3:0] pdata;
8 wire [3:0] pdatabuf;
9 wire start_flag,end_flag;
10 wire [5:0] mstate;
11
12 sigdata M0
13 (
14 .rst_n(rst_n),
15 .sclk(sclk),
16 .data(data),
17 .ask_for_data(ack)
18 );
19
20 ptosda M1
21 (
22 .rst_n(rst_n),
23 .sclk(sclk),
24 .ack(ack),
25 .scl(scl),
26 .sda(sda),
27 .data(data)
28 );
29
30 out16hi M2
31 (
32 .scl(scl),
33 .sda(sda),
34 .outhigh(outhigh),
35 .pdata(pdata),
36 .pdatabuf(pdatabuf),
37 .start_flag(start_flag),
38 .end_flag(end_flag),
39 .mstate(mstate)
40 );
41
42 endmodule
仿真结果:
在模块M2的状态机中,如果if(start_flag)没有加上else mstate<=bit0;就会发生状态跳转不正常的现象,只有第一次状态转移正确。
1 always @ (posedge scl)
2 begin
3 if(start_flag)
4 case(mstate)
5 bit0:
6 begin
7 mstate <= bit1;
8 pdata[3] <= sda;
9 $display("I am is sda bit0");
10 end
11 bit1:
12 begin
13 mstate <= bit2;
14 pdata[2] <= sda;
15 $display("I am is sda bit1");
16 end
17 bit2:
18 begin
19 mstate <= bit3;
20 pdata[1] <= sda;
21 $display("I am is sda bit2");
22 end
23 bit3:
24 begin
25 mstate <= bit4;
26 pdata[0] <= sda;
27 $display("I am is sda bit3");
28 end
29 bit4:
30 begin
31 mstate <= bit0;
32 $display("I am is sda stop");
33 end
34 default:
35 mstate <= bit0;
36 endcase
37 end
就会出现下面的结果:
FSM的设计,用状态变量来记住曾经发生过的事情,这些曾经发生过的事情对于电路下一步时钟操作有非常重要的作用。
在本例程中,无论M1和M2模块的设计都必须用状态变量记住目前所处的状态,才能正确地控制输入和输出。可综合模块的设计必须在电路总体结构明确的情况下,
用状态机写出控制的节拍和步骤后才能进行。
路漫漫其修远兮,吾将上下而求索