利用状态机实现比较复杂的接口设计:
这是一个将并行数据转换为串行输出的变换器,利用双向总线输出。这是由EEPROM读写器的缩减得到的,首先对I2C总线特征介绍:
I2C总线(inter integrated circuit)双向二线制串行总线协议为:只有总线处于“非忙”状态时,数据传输才开始。在数据传输期间,只要时钟线为高电平,数据线都必须保持稳定,否则数据线上的任何变化都被当作“启动”或“停止”信号。
下面介绍A、B、C、D的工作状态:
(1)总线处于非忙状态(A段):该段内的数据线(sda)和时钟线(scl)都保持高电平;
(2)启动数据传输(B段):当时钟线(scl)为高电平时,数据线(sda)由高电平变为低电平的下降沿被认为是“启动”信号;
(3)停止数据传输(C段):当时钟线(scl)为高电平时,数据线(sda)由低电平变为高电平的上升沿被认为是“停止”信号;
(4)数据有效(D段):在出现“启动”信号之后,在时钟线(scl)为高电平时,数据线是稳定的,这是数据线上的数据就是要传送的数据,数据线上的数据改变必须在时钟线(scl)为低电平期间完成,每个数据占用一个时钟;
(5)应答信号:每个正在接受数据的EEPROM在接收到一个字节的数据后,通常需要发出一个应答信号;而每个正在发送数据的EEPROM在发出一个字节的数据后,通常需要接受一个应答信号;EEPROM读写控制器必须提供一个与这个应答信号相联系的二外的始终脉冲。
其控制字节一共有8位:1010xxxW/R 其中1010是I2C总线器件特征编码,xxx表示地址,W/R表示读写状态。
在实现并行输入串行输出时,需要两个状态机:
主状态机主要控制内部存储器和输入端的连接,以及给出应答信号;从状态机主要负责总线连接时,内部寄存器的最高位输出个移位;
状态机的源码如下:
1 module parallel_to_serial(rst,clk,addr,data,sda,ack);
2 input rst,clk;
3 input [7:0]data,addr;
4
5 inout sda; //data bus
6 output ack; //ask for next address/data writting wo eeprm;
7 reg link_write; //whether connect to output
8 reg [2:0]state; //main status,
9 reg [4:0]sh8out_state; //serial output status
10 reg [7:0]sh8out_buf; //output data buffer
11 reg finish_F; //whether finished an operation of main status
12 reg ack;
13
14 parameter idle=0, addr_write=3'd1, data_write=3'd2, stop_ack=3'd4; //main status code
15 parameter bit0=1, bit1=2, bit2=3, bit3=4, bit4=5, bit5=6, bit6=7, bit7=8; //serial output status code
16
17 assign sda=link_write?sh8out_buf[7]:1'bz; //???????????
18
19 always @(posedge clk)
20 begin
21 if(!rst) //reset
22 begin
23 ack<=0;
24 link_write<=0; //???????
25 finish_F<=0;
26 state<=idle;
27 sh8out_state<=idle;
28 sh8out_buf<=0;
29 end
30 else
31 case(state)
32 idle:begin
33 link_write<=0; //??????
34 ack<=0;
35 finish_F<=0;
36 sh8out_buf<=addr; //???????
37 sh8out_state<=idle;
38 state<=addr_write; //???????
39 end
40 addr_write:begin
41 if (finish_F==0) begin shift8_out;end //???????
42 else
43 begin
44 link_write<=0;
45 ack<=0;
46 finish_F<=0;
47 sh8out_buf<=data; //???????
48 state<=data_write;
49 sh8out_state<=idle;
50 end
51 end
52 data_write:begin
53 if (finish_F==0) begin shift8_out;end //???????
54 else
55 begin
56 link_write<=0;
57 finish_F<=0;
58 state<=stop_ack;
59 ack<=1; //????????
60 end
61 end
62 stop_ack:begin //????
63 ack<=0;
64 state<=idle;
65 end
66 endcase
67 end
68
69 task shift8_out; //???????
70 begin
71 case(sh8out_state)
72 idle:begin
73 link_write<=1; //?????????????????17?assign sda=link_write?sh8out_buf[7]:1'bz; sda??????????sh8out_buf?????
74 sh8out_state<=bit7;
75 end
76 bit7:begin
77 link_write<=1;
78 sh8out_buf=sh8out_buf<<1; //?????data?????bit6
79 sh8out_state<=bit6;
80 end
81 bit6:begin
82 link_write<=1;
83 sh8out_buf=sh8out_buf<<1;
84 sh8out_state<=bit5;
85 end
86 bit5:begin
87 link_write<=1;
88 sh8out_buf=sh8out_buf<<1;
89 sh8out_state<=bit4;
90 end
91 bit4:begin
92 link_write<=1;
93 sh8out_buf=sh8out_buf<<1;
94 sh8out_state<=bit3;
95 end
96 bit3:begin
97 link_write<=1;
98 sh8out_buf=sh8out_buf<<1;
99 sh8out_state<=bit2;
100 end
101 bit2:begin
102 link_write<=1;
103 sh8out_buf=sh8out_buf<<1;
104 sh8out_state<=bit1;
105 end
106 bit1:begin
107 link_write<=1;
108 sh8out_buf=sh8out_buf<<1;
109 sh8out_state<=bit0;
110 end
111 bit0:begin
112 link_write<=0;
113 finish_F<=1;
114 end
115 endcase
116 end
117 endtask
118 endmodule
测试程序:
1 `timescale 1ns/1ns
2 `define clk_period 50
3 module parallel_to_serial_test;
4 reg rst,clk;
5 reg [7:0]data,addr;
6 wire ack,sda;
7 wire [2:0]state; //main status,
8 wire [4:0]sh8out_state;
9
10 initial
11 begin
12 clk=0;
13 rst=1;
14 data=0;
15 addr=0;
16 #(2*`clk_period) rst=0;
17 #(2*`clk_period) rst=1;
18 #(100*`clk_period) $stop;
19 end
20
21 always #50 clk=~clk;
22
23
24
25 always @(posedge clk)
26 begin data=data+1; addr=addr+1; end
27
28 parallel_to_serial m(
29 .rst(rst),
30 .clk(clk),
31 .addr(addr),
32 .data(data),
33 .sda(sda),
34 .ack(ack)
35 );
36
37 assign state=m.state;
38 assign sh8out_state=m.sh8out_state;
39 endmodule
波形信号: