BackEnd Code Generation in SPARK
Center for Embedded Computer Systems Computer Science and Engineering
Microelectronic Embedded Systems Laboratory University Of California, San Diego
menu Home Methodology Download News Publications Benchmarks Links Internal

Designers usually prefer to partition the finite state machine into several components. In the context of Progammable Logic Array (PLA) based FSM designs, typically a sequencing PLA for next state generation and a command PLA for primary output generation are used. In FSM design using VHDL, three functional blocks are used, namely, the next-state conditioning logic, the state-register (or state-variable) creation and the output conditioning logic.

We found this approach to be best suited for synthesizable VHDL generation, since it cleanly partitions the controller from the data path. After construction of the finite state machine from the scheduled hierarchical task graph, our framework outputs VHDL in the form of three processes (as shown in Figure 1):

  • Synchronous Process: This process assigns the next state to the current state register of the FSM. It also has an asynchronous reset which resets the state machine to a known state (the initial idle state). We have chosen a synchronous process model that is triggered at the rising edge of the clock. This process is standard for all the VHDL code generated by the Spark framework.
  • FSM Process: Based on the current state and the prevailing conditions in the data path, this combinational process determines the next state of the FSM and stores it in a Next State register. This process is generated using the state table constructed by the algorithm in the previous section.
  • Data Path Process: This process executes the operations in the data path based on the current state. Since we are using a global slicing methodology, the operations executed also depend on which branch of the mutually exclusive conditional block is active. This process is also generated using the state table (as per the scheduled HTG).
  • The data path process is also triggered at the rising edge of the clock. An alternate style would have been to trigger it by a change in the current state. However, this leads to all the data path activation signals being latched on the current state. This can cause glitches and discrepancies between simulation and synthesis results.

    There are several advantages of partitioning the FSM and data path in this manner. Data paths are generally regular and either use components picked from a library (either standard cell or custom) or synthesized by module compilers. So designers often avoid trying to flatten the structural hierarchy inherent in the data path. On the other hand, designers prefer to flatten the control FSM and try to optimize it as much as possible, since critical paths typically end up lying on forwarding paths. Furthermore, frequently, there are data path components that are custom designed. So during the early design phase, a functional equivalent is used in the RTL synthesis process. The custom designed component can then be plugged into the netlist at a later stage. This process partitioning thus facilitates easy application of different constraints and synthesis commands for the control FSM and the data path.


    Figure 1: Synthesizable Register Tranfer Level VHDL (a) Main architecture body with all three processes (b) The Current state assignment process with synchronous reset (c) The Next state calculation process (d) The Data path operation process}}

    Besides these four processes, the VHDL code for the multiplexing logic that feeds into the functional units is placed after all the processes in the architecture description of a component. We have experimented with several styles for this multiplexing logic and have settled on a style that has a process for each input port of a functional unit. The select signals for the multiplexer are determined based on the control state and the conditions under which a data value feeds into the input port of the functional unit. The select signals are thus used to choose between the various data inputs.

    An example of the VHDL output for one input port of a comparator is given below:

      res_CMP_4_in0_MUXES: PROCESS(CURRENT_STATE, regNum1, hT0, regNum0, hT1, hT39, regNum6) 
        variable mux_select : std_logic_vector(7 downto 0);
      BEGIN
        mux_select := "00000000";
        if (CURRENT_STATE(1) = '1') then
          mux_select(0) := '1';
        end if;
        if (CURRENT_STATE(3) = '1' and hT0) then
          mux_select(1) := '1';
        end if;
        if (CURRENT_STATE(4) = '1' and hT0 and hT1 and NOT(hT39)) then
          mux_select(2) := '1';
        end if;
        if (CURRENT_STATE(5) = '1' and hT0 and hT1 and NOT(hT39)) then
          mux_select(3) := '1';
        end if;
        case mux_select is
          when "00000001" => 
            res_CMP_4_in0 <= regNum1;
          when "00000010" => 
            res_CMP_4_in0 <= regNum0;
          when "00000100" => 
            res_CMP_4_in0 <= regNum1;
          when "00001000" => 
            res_CMP_4_in0 <= regNum6;
          when others => 
            res_CMP_4_in0 <= 0;
        END CASE;
      END PROCESS;         -- res_CMP_4_in0_MUXES END PROCESS;
    

    Maintained by Sumit Gupta <sumitg@cecs.uci.edu>