利用狀態機實現比較復雜的接口設計:
這是一個將並行數據轉換為串行輸出的變換器,利用雙向總線輸出。這是由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
波形信號: