This particular example is verilog, but my question is more about the state machine structuring, which would be relevant to both VHDL and verilog.
So if I have a state machine, this one is fairly simple:
always@(posedge m_axi_clk) begin if(m_axi_resetn == 0) begin current_state <= S_IDLE; end else begin if ((ps_axi_busy == 0) && (ps_wr_en_i == 0)) begin case(current_state) S_IDLE: current_state <= S_WAIT_FOR_CONFIG; S_WAIT_FOR_CONFIG: if (config_done == 1) current_state <= S_WAIT_FOR_DMA; else current_state <= S_WAIT_FOR_CONFIG; ... S_HALTED: current_state <= S_HALTED; default: current_state <= S_HALTED; endcase end end end
If I want to perform, for example, a BRAM write triggered from the state machine state, I usually split the processes up, so I'll duplicate the entire process and then do the following:
always@(posedge m_axi_clk) begin if(m_axi_resetn == 0) begin ps_wr_addr_i <= 0; ps_wr_data_i <= 0; ps_wr_en_i <= 0; end else begin if ((ps_axi_busy == 0) && (ps_wr_en_i == 0)) case(current_state) S_IDLE: begin ps_wr_addr_i <= `INPUT_NEXT; ps_wr_data_i <= ones; ps_wr_en_i <= 1; end S_WAIT_FOR_CONFIG: begin ps_wr_addr_i <= 0; ps_wr_data_i <= 0; ps_wr_en_i <= 0; if (config_done == 1) begin ps_wr_addr_i <= `INPUT_DATA; ps_wr_data_i <= ones; ps_wr_en_i <= 1; end end ... default: begin ps_wr_addr_i <= 0; ps_wr_data_i <= 0; ps_wr_en_i <= 0; end endcase // case (current_state) else begin ps_wr_addr_i <= 0; ps_wr_data_i <= 0; ps_wr_en_i <= 0; end // else: !if(ps_axi_busy == 0) end // else: !if(m_axi_resetn == 0
I used to do one big process, where I merge everything together, but that gets big fast for multiple interfaces and multiple states. Then it's also difficult to sift through the signals to check that each write is in the right place.
I usually take out all the states where writes don't occur and bunch them up together under
default. This works nicely for small state machines, although if I do a write on a state edge, then I will need to maintain that condition through both processes. For example, I only switch out of
S_WAIT_FOR_CONFIG (and do the write) when
config_done is asserted. This condition is now in twice, and I have to change it in two places (something I'd like to avoid, if possible).
I've also been thinking about creating a delayed state signal and doing an edge detect on that in order to trigger certain BRAM writes, this allows the
config_done to trigger the state switch, and then that edge triggers the writes. This is great for writes on state switch edges, but I can't do the same thing for writes that are in the middle of states.
There's also the asynchronous option:
assign ps_wr_addr_i = ((current_state == S_IDLE) && (ps_axi_busy == 0)) ? `INPUT_NEXT: ((current_state == S_WAIT_FOR_CONFIG) && (config_done) && (ps_axi_busy == 0)) ? `INPUT_DATA: 0; ...
I prefer this from a readability/elegance standpoint, but due to the timing implications (no longer pipelined), I don't really use it that often. Also, once again the
config_done check needs to be maintained in multiple places.
I understand that there are subtle variations in the behaviour of each of these three, so small adjustments will probably need to be made for similar behaviour overall. Also I think it depends on what is needed at the time as far as pipelining goes. However, I'm more interested behind the principal behind these writes.
Is there a better way of doing this, or is this it? How do I handle scaling this state machine for a more complicated protocol where writes are necessary perhaps at the beginning, middle, or end of a state? Do I make separate states for that, or do I use edge triggers/if statements? How do write this in an elegant way and stop it from just running away into an abomination of a state machine?
TL;DR I usually use a state machine as a way of setting up the framework of what gets done when, and then all the other signals revolve around that. I'm struggling to consolidate this "macro" framework with the "micro" details that involve timing multiple writes during one state. Am I thinking about this wrong and can I be using a state machine in a better way?