Search code examples
haskellmonadsrecordclash

How to refactor code using the State monad in order to increase modularity by adding more functions?


I am trying to get the hang of monads. I figured it would be good practice to play with the state monad.

Using the UART Rx from CLaSH:

-- UART RX Logic
data RxReg
  = RxReg
  { _rx_reg        :: BitVector 8
  , _rx_data       :: BitVector 8
  , _rx_sample_cnt :: Unsigned 4
  , _rx_cnt        :: Unsigned 4
  , _rx_frame_err  :: Bool
  , _rx_over_run   :: Bool
  , _rx_empty      :: Bool
  , _rx_d1         :: Bit
  , _rx_d2         :: Bit
  , _rx_busy       :: Bool
  }

makeLenses ''RxReg

uartRX r@(RxReg {..}) rx_in uld_rx_data rx_enable = flip execState r $ do
  -- Synchronise the async signal
  rx_d1 .= rx_in
  rx_d2 .= _rx_d1
  -- Uload the rx data
  when uld_rx_data $ do
    rx_data  .= _rx_reg
    rx_empty .= True
  -- Receive data only when rx is enabled
  if rx_enable then do
    -- Check if just received start of frame
    when (not _rx_busy && _rx_d2 == 0) $ do
      rx_busy       .= True
      rx_sample_cnt .= 1
      rx_cnt        .= 0
    -- Star of frame detected, Proceed with rest of data
    when _rx_busy $ do
      rx_sample_cnt += 1
      -- Logic to sample at middle of data
      when (_rx_sample_cnt == 7) $ do
        if _rx_d1 == 1 && _rx_cnt == 0 then
          rx_busy .= False
        else do
          rx_cnt += 1
          -- start storing the rx data
          when (_rx_cnt > 0 && _rx_cnt < 9) $ do
            rx_reg %= replaceBit (_rx_cnt - 1) _rx_d2
          when (_rx_cnt == 9) $ do
            rx_busy .= False
            -- Check if End of frame received correctly
            if _rx_d2 == 0 then
              rx_frame_err .= True
            else do
              rx_empty     .= False
              rx_frame_err .= False
              -- Check if last rx data was not unloaded
              rx_over_run  .= not _rx_empty
  else do
    rx_busy .= False

How would I go about moving the when logic to their own functions? I have been playing with this for a little bit, but the lenses seem to be causing issues.

Could not deduce Control.Monad.State.Class.MonadState from .=

I am think there is a function here

That I am missing.

I would want to do something like

newFun = when (not _rx_busy && _rx_d2 == 0) $ do
         rx_busy       .= True
         rx_sample_cnt .= 1
         rx_cnt        .= 0

I would think there would be a function like execState that I need.

So my questions are,

  • What should I be using?
  • What does that function do that allows it to compose with other functions?
  • If monads are just composing why cant I have the when in a function without an execState type function?
  • What other ways could I increase the modularity of this code?

Solution

  • In your definition of newFun

    newFun = when (not _rx_busy && _rx_d2 == 0) $ do
             rx_busy       .= True
             rx_sample_cnt .= 1
             rx_cnt        .= 0
    

    you are trying to refer to _rx_busy and _rx_d2, which are record fields of the type RxReg. In uartRX, the first argument is an RxReg, and the RecordWildCards language extension is used to bind all the field names by matching the argument with the pattern RxReg{..}. So you will either need to pass r itself, or at least _rx_busy and _rx_d2, from uartRX to newFun; i.e. either have

    newFun busy d2 = when (not busy && d2 == 0) $ do
        ...
    

    or

    newFun RxReg{..} = when (not _rx_busy && _rx_d2 == 0) $ do
        ...