為了更好了解並除錯i2c_master_byte_ctrl
這個模組,
我建立簡單的testbench檔案
`include "i2c_master_byte_ctrl.v" `include "i2c_master_bit_ctrl.v" `include "i2c_master_defines.v"
module _;
parameter PERIOD = 10 ;
reg clk = 0 ; reg rst = 1 ; reg nReset = 0 ; reg ena = 1 ; reg [15:0] clk_cnt = 5 ; reg start = 0 ; reg stop = 0 ; reg read = 0 ; reg write = 0 ; reg ack_in = 0 ; reg [7:0] din = 0 ; reg scl_i = 1 ; reg sda_i = 1 ;
wire cmd_ack ; wire ack_out ; wire i2c_busy ; wire i2c_al ; wire [7:0] dout ; wire scl_o ; wire scl_oen ; wire sda_o ; wire sda_oen ;
initial begin forever #(PERIOD/2) clk=~clk; end
initial begin #(PERIOD*2) nReset = 1; end
i2c_master_byte_ctrl __ ( .clk ( clk ), ... 省略 .sda_oen ( sda_oen ) );
initial begin $dumpfile("test.vcd"); $dumpvars(0);
end
initial begin #6000 $finish; end
endmodule
|
狀態跳轉測試
code
- rst 依序放掉
- 給Command Register 訊號 Start+Read
- 收到 byte ack 後放掉
- 在一段時間後停止模擬
initial begin $dumpfile("test.vcd"); $dumpvars(); #5 nReset = 1; #5 rst = 0; #3 start = 1; read = 1; wait(cmd_ack) start = 0; read = 0; end
initial begin #4000 $finish; end
|
先看起始部分:
- clk: 系統頻率
- 沒寫出來的scl_i, sda_i: 暫時設high以防影響.
- scl_oen, sda_oen: bit ctrl 的輸出, 根據其state會有不同輸出.
- c_state: (byte ctrl 的)
- 因為 byte ctrl 使用系統clk, 很快就跳到 ST_IDLE, 並且指定start指令給 bit ctrl
- 等待 bit ctrl 的
core_ack
, 改成跳到 ST_START, 並且指定read指令給 bit ctrl
- core_ack: bit ctrl 每完成一個 bit 指令, 就會回傳
core_ack
(內部ack) 給 byte ctrl
- clk_en: 為 bit ctrl 狀態機跳動的頻率, 經prescale value除頻, 為了方便觀察設為 5, 所以只跳動5次
- bit controller.c_state: bit ctrl 的 狀態, 會在每一次
clk_en
之後改變
請特別注意此處的state, 除了IDLE, 即使已經在該state, 收到core_ack
才做出動作
寫入測試
code
initial begin $dumpfile("test.vcd"); $dumpvars(); assign scl_i = scl_oen; sda_i = 1; #5 nReset = 1; #5 rst = 0; #5 start = 1; write = 1; din = 8'b00111011; wait( __.c_state == 8 ) sda_i = 0; wait(cmd_ack | i2c_al) start = 0; write = 0; din = 8'b0; sda_i = 1; end
initial begin #4000 $finish; end
|
假設寫入為slave地址, 0011101, W/R 為 1 代表想要讀取該地址的資訊
clk: 系統頻率 scl_i, sda_i: slave 的 scl, sda scl_oen, sda_oen: bit ctrl 輸出, 在SCL為高時保持SDA值 core_ack: bit ctrl 每完成一個 bit 指令, 就會回傳 內部ack 給 byte ctrl c_state: byte ctrl 的狀態, 使用系統clk din: 地址 + W/R ack_out: 傳遞到 top 的 ack 訊號
|
寫入7-bit addr, 在寫入1 bit W/R, 接著收ACK
Aribitration Lost
code
initial begin #200 repeat(9) begin #180 scl_i = ~scl_i; #180 scl_i = ~scl_i; end end
initial begin @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 1; end
initial begin $dumpfile("test.vcd"); $dumpvars(); #5 nReset = 1; #5 rst = 0; #5 start = 1; write = 1; din = 8'b00111011; wait( __.c_state == 8 ) sda_i = 0; wait(cmd_ack | i2c_al) start = 0; write = 0; din = 8'b0; sda_i = 1; end
|
當aribitration Lost發生時, master本身進入 ST_IDLE
, 下空指令 I2C_CMD_NOP
slave_wait
code
initial begin #200 #200 scl_i = 0; #600 scl_i = 1; repeat(9) begin #200 scl_i = ~scl_i; #400 scl_i = ~scl_i; end end
initial begin @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 1; end
|
slave_wait 會讓 bit ctrl rst, 慢速 clock 重算.
直到 iSCL 為 high 才重新計算
scl_sync
code
initial begin #200 #200 scl_i = 0; repeat(9) begin #180 scl_i = ~scl_i; #100 scl_i = ~scl_i; end end
initial begin @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 0; @(negedge scl_i) #100 sda_i = 1; @(negedge scl_i) #100 sda_i = 1; end
|
在oSCL HIGH時, 若發現 iSCL negedge 則讓 慢速clk 重算.