狀態(tài)機(jī)的可監(jiān)控維度該如何考慮
》狀態(tài)機(jī)該怎么監(jiān)控
最近遇到一個(gè)關(guān)于狀態(tài)機(jī)的問(wèn)題,具體的業(yè)務(wù)就不講了。關(guān)于FSM怎么寫(xiě)這種初級(jí)問(wèn)題在這里也不講了。這里我們只關(guān)注下在真實(shí)的應(yīng)用場(chǎng)景里,從監(jiān)控的角度來(lái)看,該如何去看待FSM。 FPGA設(shè)計(jì)里,除了功能實(shí)現(xiàn)之外,最重要的一部分就是DFX的設(shè)計(jì)。畢竟燒錄完之后我們也只能通過(guò)DFX來(lái)去觀(guān)測(cè)內(nèi)部的狀態(tài)(說(shuō)JTAG的請(qǐng)繞過(guò),那頂多是開(kāi)發(fā)階段)。在考慮資源允許的情況下,我們要做的應(yīng)當(dāng)是有充分的DFX能夠幫助我們?nèi)ビ^(guān)測(cè)內(nèi)部的狀態(tài)。一個(gè)工程做下來(lái)往往是在補(bǔ)DFX時(shí)覺(jué)得要添加的太多了,而到真實(shí)需要定位問(wèn)題的時(shí)候則又會(huì)感嘆“當(dāng)初怎么不多添加些DFX”~ 回到對(duì)FSM的處理,一開(kāi)始想到的可能是:
把FSM的當(dāng)前狀態(tài)添加到DFX中。
沒(méi)毛病,我們能夠清楚的觀(guān)察到當(dāng)前狀態(tài)機(jī)處于什么狀態(tài)。但當(dāng)狀態(tài)機(jī)如果處于卡死不動(dòng)的狀態(tài),那我們所需要的就是導(dǎo)致當(dāng)前狀態(tài)卡住的信號(hào)的狀態(tài):
把FSM各條件跳轉(zhuǎn)的判斷信號(hào)添加到DFX中。
上面兩條也基本是我們之前設(shè)計(jì)常常會(huì)做的內(nèi)容。然而這里面只是對(duì)下面的場(chǎng)景做了監(jiān)控:
狀態(tài)機(jī)卡住的場(chǎng)景——通過(guò)狀態(tài)跳轉(zhuǎn)條件的DFX信號(hào)去判斷卡住的原因
對(duì)于DFX信號(hào),像我們通過(guò)PCIe寄存器鏈路去讀取DFX信號(hào)時(shí)不可能獲取到每拍的結(jié)果,因而上面的DFX信號(hào)添加方式也就只能針對(duì)FSM卡死的情況進(jìn)行定位判斷。然而很不幸,這一次我遇到了一個(gè)狀態(tài)機(jī)在跳轉(zhuǎn),只是沒(méi)有跳轉(zhuǎn)到一個(gè)需要響應(yīng)某個(gè)動(dòng)作的狀態(tài)。由于代碼從別人那里接手過(guò)來(lái)的,看代碼也能對(duì)case場(chǎng)景進(jìn)行一個(gè)判斷。但回到監(jiān)控的角度,只是沒(méi)有跳轉(zhuǎn)到一個(gè)需要響應(yīng)某個(gè)動(dòng)作的狀態(tài)這個(gè)判斷是我針對(duì)看到的DFX抓取信號(hào)來(lái)判斷得到的結(jié)論,然而我并不能自證(當(dāng)然可以通過(guò)仿真構(gòu)造類(lèi)似Case來(lái)進(jìn)行驗(yàn)證,這不提了),畢竟DFX不是每拍的結(jié)果都能看得到的,針對(duì)線(xiàn)上的問(wèn)題,所有的判斷應(yīng)當(dāng)都是有充足的證據(jù)的,而不是結(jié)合觀(guān)測(cè)加推斷。那么針對(duì)這種場(chǎng)景,有必要再增加一種監(jiān)控手段:
記錄各狀態(tài)之間是否有過(guò)跳轉(zhuǎn)發(fā)生,軟件可清零。
通過(guò)記錄各狀態(tài)之間是否發(fā)生過(guò)跳轉(zhuǎn),那么我們可以結(jié)合DFX當(dāng)前狀態(tài)來(lái)充分說(shuō)明某個(gè)狀態(tài)沒(méi)有到達(dá)。而記錄各狀態(tài)之間是否有過(guò)跳轉(zhuǎn)發(fā)生,所消耗的資源也非常少。 》example
來(lái)看一個(gè)簡(jiǎn)單的狀態(tài)機(jī):
import spinal.lib.fsm._ classTopLevelextendsComponent{ val io = newBundle { val result = out Bool() } val fsm = newStateMachine { val counter = Reg(UInt(8bits)) init(0) io.result := False val stateA : State = newState with EntryPoint { whenIsActive(goto(stateB)) } val stateB : State = newState { onEntry(counter := 0) whenIsActive { counter := counter + 1 when(counter === 4) { goto(stateC) } } onExit(io.result := True) } val stateC : State = newState { whenIsActive(goto(stateA)) } } }
這里面會(huì)存在三個(gè)狀態(tài)StateA、StateB、StateC:
StateA——>StateB
StateB——>StateC
StateC——>StateA
那么我們需要記錄的就是:
StateA_to_fsm_stateB_change
StateB_to_fsm_stateC_change
StateC_to_fsm_stateA_change
在SpinalHDL里,這種活兒還是不要手動(dòng)的好,當(dāng)然是自動(dòng)化的處理好。下面給一個(gè)Demo,可能有大神有更加優(yōu)雅的解決方式,歡迎交流。
定義StateExtend:
class StateExtend(implicit stateMachineAccessor: StateMachineAccessor) extends State { val nextStateBuffer=Set[StateExtend]() def goto(state:StateExtend)={ nextStateBuffer.add(state) stateMachineAccessor.goto(state) } }
主要是在原有State的基礎(chǔ)上重定義了goto函數(shù),記錄了每個(gè)狀態(tài)會(huì)跳轉(zhuǎn)的下一狀態(tài)。
然后定義FsmMonitor:
caseclassFsmState(monReg:Bool,curState:State,nextState:State) caseclassFsmMonitor(implicitstateMachineAccessor: StateMachine) extendsArea{ val stateMonMap=Map[State,ArrayBuffer[FsmState]]() val state_mon_clear=RegInit(False) simPublic() defgenerateFsmMonitor()={ val current_state_dly=RegNext(stateMachineAccessor.stateReg) val next_state_dly=RegNext(stateMachineAccessor.stateNext) for(state<-stateMachineAccessor.states){ ??????if(state.isInstanceOf[StateExtend]){ ????????for?(nextState <- state.asInstanceOf[StateExtend].nextStateBuffer){ ??????????val state_change=RegInit(False) setName (s"${state.getName()}_to_${nextState.getName()}_change") ??????????when(state_mon_clear){ ????????????state_change.clear() ??????????}elsewhen((current_state_dly===stateMachineAccessor.enumOf(state))){ ????????????when(next_state_dly===stateMachineAccessor.enumOf(nextState)){ ??????????????state_change.set() ????????????} ??????????} ??????????stateMonMap.getOrElse(state,ArrayBuffer[FsmState]()).append(FsmState(state_change,state,nextState)) ????????} ??????} ????} ??} }
在generateFsmMonitor中,會(huì)針對(duì)每個(gè)狀態(tài)來(lái)分別創(chuàng)建跳轉(zhuǎn)相應(yīng)的跳轉(zhuǎn)監(jiān)控信號(hào),并記錄到stateMonMap中去。state_mon_clear可用于清零狀態(tài)所有監(jiān)控信號(hào)。通過(guò)regif可講state_mon_clear及stateMonMap中的所有元素添加到寄存器總線(xiàn)中去(也可以直接用regif聲明創(chuàng)建寄存器)。
最終,在使用時(shí)如下即可:
case classfsmTest() extendsComponent{ val counter = out(Reg(UInt(8bits)) init (0)) val fsm = newStateMachine { val stateA = newStateExtend() with EntryPoint setName("StateA") val stateB, stateC = newStateExtend() stateA.whenIsActive { stateA.goto(stateB) } stateB.whenIsActive { stateB.goto(stateC) } stateC.onEntry(counter := 0) stateC.whenIsActive { counter := counter + 1 when(counter === 3) { stateC.goto(stateA) } } val fsm_mon=FsmMonitor() addPrePopTask(()=>{ fsm_mon.generateFsmMonitor() }) } }
差別點(diǎn)在于goto換成對(duì)應(yīng)的StateA.goto等顯示調(diào)用的形式。通過(guò)例化FsmMonitor調(diào)用generateFsmMonitor即可注冊(cè)所有的狀態(tài)跳轉(zhuǎn)信號(hào):
審核編輯:黃飛
-
FPGA
+關(guān)注
關(guān)注
1620文章
21510瀏覽量
598877 -
寄存器
+關(guān)注
關(guān)注
31文章
5250瀏覽量
119194 -
狀態(tài)機(jī)
+關(guān)注
關(guān)注
2文章
489瀏覽量
27391
原文標(biāo)題:反思一下FSM
文章出處:【微信號(hào):Spinal FPGA,微信公眾號(hào):Spinal FPGA】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論