I implemented a FSM in vhdl using two processes;
- A sync process for state transition
RegProxy_FSM_Proc : process (USB_CLK)
begin
if falling_edge(USB_CLK) then
if (USB_CLR = '1') then
RegProxyUSBState <= IDLE;
else
case RegProxyUSBState is
when IDLE =>
if USB3_RXF_L = '0' then
RegProxyUSBState <= OP_PRE_INIT;
elsif USB3_TXE_L = '0' then
case OffsetReg(11 downto 8) is
when "1110" =>
RegProxyUSBState <= FIFO_SEND_INIT;
when "1111" =>
RegProxyUSBState <= ATOM4_SEND_INIT;
when others =>
RegProxyUSBState <= REG_SEND_INIT;
end case;
end if;
when OP_PRE_INIT => RegProxyUSBState <= OP_INIT;
when OP_INIT => RegProxyUSBState <= GET_OPCODE_OFFSET;
when GET_OPCODE_OFFSET => RegProxyUSBState <= GET_LENGTH;
when GET_LENGTH =>
case OpCodeReg(3 downto 2) is
when "00" =>
RegProxyUSBState <= GET_DATA_LSW;
when "01" =>
RegProxyUSBState <= GET_MASK_LSW;
when others =>
RegProxyUSBState <= IDLE;
end case;
when GET_DATA_LSW => RegProxyUSBState <= GET_DATA_MSW;
when GET_MASK_LSW => RegProxyUSBState <= GET_MASK_MSW;
when GET_DATA_MSW =>
if RegLengthCountNotEmpty = '1' then
RegProxyUSBState <= GET_DATA_LSW;
else
RegProxyUSBState <= IDLE;
end if;
when REG_SEND_INIT => RegProxyUSBState <= REG_SEND_LSW;
when REG_SEND_LSW => RegProxyUSBState <= REG_SEND_MSW;
when FIFO_SEND_INIT => RegProxyUSBState <= FIFO_SEND;
when ATOM4_SEND_INIT => RegProxyUSBState <= ATOM4_SEND;
when REG_SEND_MSW =>
if RegLengthCountNotEmpty = '1' then
RegProxyUSBState <= REG_SEND_LSW;
else
RegProxyUSBState <= IDLE;
end if;
when ATOM4_SEND | FIFO_SEND =>
if Atom4LengthCountNotEmpty = '0' then
RegProxyUSBState <= IDLE;
end if;
when others =>
RegProxyUSBState <= IDLE;
end case;
end if;
end if;
end process RegProxy_FSM_Proc;
- A combinational process for setting outputs (and reading inputs)
USB_CLK_PROC : process (RegProxyUSBState, USB_DATA)
begin
case RegProxyUSBState is
when IDLE | OP_PRE_INIT | OP_INIT =>
OpCodeReg <= (others => '0');
OffsetReg <= (others => '0');
LengthReg <= (others => '0');
RegPointer <= (others => '0');
RegLengthCounter <= (others => '0');
Atom4LengthCounter <= (others => '0');
FifODataReady <= '0';
when GET_OPCODE_OFFSET =>
OpCodeReg <= USB3_DATA(3 downto 0);
OffsetReg <= USB3_DATA(15 downto 4);
when GET_LENGTH =>
RegLengthCounter <= unsigned(USB3_DATA(11 downto 0));
RegPointer <= unsigned(OffsetReg);
if F_MemSelect = '1' then
Atom4LengthCounter(13 downto 2) <= unsigned(USB3_DATA(11 downto 0));
Atom4LengthCounter( 1 downto 0) <= (others => '0');
else
Atom4LengthCounter <= ATOM4_LEN_COUNT_FOUR;
end if;
when GET_DATA_LSW | REG_SEND_LSW =>
if RegLengthCountNotEmpty = '1' then
RegLengthCounter <= RegLengthCounter - REG_LEN_COUNT_ONE;
end if;
when GET_DATA_MSW =>
if RegLengthCountNotEmpty = '1' then
RegPointer <= RegPointer + REG_PTR_ONE;
end if;
when ATOM4_SEND =>
if Atom4LengthCountNotEmpty = '1' and Atom4DataReady = '1' then
Atom4LengthCounter <= Atom4LengthCounter - ATOM4_LEN_COUNT_ONE;
end if;
when FIFO_SEND =>
if Atom4LengthCountNotEmpty = '1' then
Atom4LengthCounter <= Atom4LengthCounter - ATOM4_LEN_COUNT_ONE;
end if;
when others =>
OpCodeReg <= OpCodeReg; -- Ahmed: to avoid latches
OffsetReg <= OffsetReg; -- Ahmed: IDLE->OP_INIT Cases
LengthReg <= LengthReg; -- Ahmed: IDLE->OP_INIT Cases
RegPointer <= RegPointer; -- Ahmed: IDLE->OP_INIT Cases
RegLengthCounter <= RegLengthCounter; -- Ahmed: IDLE->OP_INIT Cases
Atom4LengthCounter <= Atom4LengthCounter; -- Ahmed: IDLE->OP_INIT Cases
FifODataReady <= FifODataReady; -- Ahmed: IDLE->OP_INIT Cases
end case;
FifODataReady <= SFDRdReq or PFDRdReq;
end process USB_CLK_PROC;
Brief explanation of signals:
- USB_CLK is the main system clock
- USB3_DATA is a 16 bit input std_logic_vector that changes at falling clock edge
- OpCodeReg: output signal that should be set to USB3_DATA(3 downto 0) when RegProxyUSBState is in GET_OPCODE_OFFSET state, then keep it's value till the FSM reaches the last state
- OffsetReg: similar to OpCodeReg, USB3_DATA(15 downto 4), and should keep its value as well
- RegLengthCounter: should take the value in GET_LENGTH state and is equal to unsigned(USB3_DATA(11 downto 0));
Problem: Unexpected 2nd transition in those signal values is observed in Modelsim as shown in the image
I then tried removing USB_DATA from the process sensitivity list and observed that OpCodeReg and OffsetReg values are now read from the previous clock cycle (Not sure why), while RegLengthCounter was read at the current clock cycle (GET_LENGTH).
Could you please explain what is happening in both cases, and how to get the OpCodeReg and OffsetReg to register correctly at the GET_OFFSET_OPCODE state?