為了更好了解並除錯i2c-master-bit-ctrl
這個模組,
我建立簡單的testbench檔案
簡略設計如下, 名稱保留與原port一樣的名稱.
|
接下來依據每一個章節分別模擬訊號,
PART0 slave_wait, scl_sync, 除頻
slave_wait (Clock Stretching)
- 目的: 符合
(scl_oen & ~dscl_oen & ~sSCL) | (slave_wait & ~sSCL);
- 方法: 選用
I2C_CMD_STOP
作為cmd輸入給模組,因為會有scl_oen
上升的狀況假設系統50MHz:#0 cmd =
#1200 scl_i = 0;
#1800 scl_i = 1; - T0: 下
I2C_CMD_STOP
指令 - T1:
scl_i
被降為0, 代表slave clock stretching - T2: 因為
I2C_CMD_STOP
指令, FSM開始動作產生STOP波形 - T3: 因為
(scl_oen & ~dscl_oen & ~sSCL)
條件成立slave_wait
拉高, master發現有clock stretching, 停止cnt, FSM不再跳動 - T4: slave離開忙碌, 恢復SLC
- T5: master在數完
filter_cnt
後, 發現sSLC
恢復了, 解除slave_wait
scl_sync (Clock Synchronization)
- 目的: 符合
dSCL & ~sSCL & scl_oen;
條件 - 方法: 選用
I2C_CMD_STOP
做為測試用,因為會有scl_oen=1
的狀況,#0 cmd =
#1200 scl_i = 0;
#1800 scl_i = 1; - T0: 下
I2C_CMD_STOP
指令 - T1: 在這個master動作時, 有其他的master把
scl_i
拉低 - T2: 經過
filter_cnt
後, 這個masterˋ終於發現有問題,趕緊synchronize
除頻
- 目的: 符合
(rst || ~|cnt || !ena || scl_sync)
除頻重置的條件 - 方法: 逐一釋放rst, en, 以及製造
scl_sync
#0 cmd =
#0 ena = 0;
#400 ena = 1;
#2200 scl_i = 0; - T0: 下
I2C_CMD_STOP
指令 - T1: 放掉
nReset
- T2: 放掉
rst
- T3: 打開
ena
- T4: 製造
scl_sync
可以看到除頻功能正常運作, 除了在T2~T3間有誤判scl_sync, 其餘工作皆正常.
PART1 訊號過濾
捕捉 cSCL, cSDA 訊號
過濾 fSCL, fSDA 訊號
產生過濾完成之sSCL, sSDA, 延遲 dSCL, dSDA
把這三個功能放在一張圖解釋
- 目的: 觀察訊號採樣(過濾)的方法
- 方法: 僅以SDA為例,讓SDA訊號變化
#1000 sda_i = 1;
#1001 sda_i = 0; - T0:
sda_i
變為1,cSDA
變化00 -> 01 -> 11 - T1:
cSDA
已經是 11, 但fSDA
頻率為1MHz比較慢, 所以要慢慢變 - T2:
fSDA
變化為011, 因為符合&fSDA[2:1] | &fSDA[1:0] | (fSDA[2] & fSDA[0])
條件, 輸出sSDA
為1 - T3:
sda_i
變為0,cSDA
變化11 -> 10 -> 00 - T4:
cSDA
已經是 00, 但fSDA
頻率為1MHz比較慢, 所以要慢慢變
可將這兩個訊號, 五個位元看作dff串,
其中若把cSDA當作T時間, 則cSDA[1]剛好會是T-20ns的訊號
而fSDA則是擷取 T-1020ns │ T-2020ns │ T-3020ns 這三個訊號的其中兩個
至於為什麼用這個時間點我無法解釋.
cSDA[0] cSDA[1] fSDA[0] fSDA[1] fSDA[2] |
若以上無法理解可參考Opencores上的i2c controller core代码解析
至於dSCL
, dSDA
僅只是過一個 DFF 作為delay訊號而已.
Part2 訊號判斷
開始結束 START, STOP
忙碌訊號 Busy
這兩個訊號也可以合併一起講
- 目的: 當外部輸入START, STOP, master要能偵測到
- 方法: 試著給外部
scl_i
,sda_i
#1000 sda_i = 1; scl_i = 1; |
- T0:
scl_i=1
時sda_i
訊號拉低, 模擬的START訊號 - T1: 經過
filter_cnt
時間後, 內部sSDA
真正拉低,sta_condition
條件滿足拉高一個clk,busy
開始 - T2:
scl_i=1
時sda_i
訊號拉高, 模擬的STOP訊號 - T3: 經過
filter_cnt
時間後, 內部sSDA
真正拉高,sto_condition
條件滿足拉高一個clk,busy
結束
爭輸贏Arbitration Lost
- 目的: 在wb_c狀態下, 讓仲裁輸掉
- 方法: 選用
I2C_CMD_WRITE
, 在wr_c 時sda_i
設為0 (Din為0)#0 cmd =
#0 din = 1;
#4000 sda_i = 0;
#1500 sda_i = 1; - T0: 收到cmd
I2C_CMD_WRITE
, 下一個state wr_a - T1: 執行wr_a, 開始拉低SCL, 給SDA Din, 下一個state wr_b
- T2: 執行wr_b, 拉高SCL, 給SDA Din, 下一個state wr_c
- T3: 執行wr_c, 拉高SCL, 給SDA Din, 開啟
sda_chk
, 馬上發現別人在這個bit是low, 輸了Arbitration 讓byte-ctrl以及top知道. - T4: 無法執行wr_d, 被強制跳回IDLE狀態, SCL, SDA維持.
PART3 訊號輸出
dout
FSM
這兩個部份就不多做sim了
有興趣的人再去下I2C_CMD_START
, I2C_CMD_STOP
, I2C_CMD_WRITE
, I2C_CMD_READ
四個command, 如同前面的cmd下法