Search code examples
entityvhdl

vhdl how to use an entity within a process


I'm having difficulties to understand how I could utilize a sequential logic entity in the process of another. This process is a state-machine which on each clock signal either reads values from the input, or performs calculations. These calculation take many iterations to complete. However, each iteration is supposed to utilize a sub-entity, which is defined using the same principles as the above one (two-state state-machine, clock-based iterations), to obtain some results needed in the same iteration.

As I see it, I have two options:

  1. implementing the subentity in a separate process within the main entity and finding a way to halt the main process and sync it with the subentity execution - this would mean using the clock signal of the main entity
  2. implementing the subentity within the process of the main entity (basically something like a function call) and finding a way to halt the main process until subentity execution completes - this seems to me hardly doable using the main clock signal

None of them seems very appealing and rather complex, so I'm asking for some experienced insight and clarification. I really hope that there is a more conventional way that I'm missing.


Solution

  • "Entity" is an unfortunate choice of word here, as it suggests a VHDL Entity which may or may not be what you want.

    You are thinking along roughly the right lines however, but it is a little unclear what you mean by "appealing"; so your goals are unclear and that makes it difficult to help.

    To take your two approaches separately :

    (1) Separate processes are a valid approach to dividing up tasks. They will naturally operate in parallel. In a synchronous design (best practice, safest and simplest - not universal but you need a compelling reason to do anything else) they will normally both be clocked by the same system clock.

    When you need to synchronise them, you can, using extra "handshaking" signals. Typically your main SM would start the subsystem, wait until the subsystem acknowledged, wait again until the subsystem was done, and use the result.

    main_sm : process(clk)
    begin
       if rising_edge(clk) then
          case state is
          ...
          when start_op =>
             subsystem_start <= '1';
             if subsystem_busy = '1' then
                state <= wait_subsystem;
             end if;
    
          when wait_subsystem <= 
             subsystem_start <= '0';
             if subsystem_busy = '0' then
                state <= use_result;
             end if;
    
          when use_result => -- carry on processing
          ...
          end case;
       end if;  
    end process main_sm;
    

    It should be clear how to write the subsystem to match...

    This is most useful where the subsystem processing takes a large, variable or unknown time to complete - perhaps sending characters to a UART, or a serial divider. With care, it can also allow several top level processes to access the subsystem to save hardware (obviously the subsystem handshaking logic only responds to one process at a time!)

    (2) If the sub-entity is to be implemented in the process, it should be written as a subprogram, i.e. as you speculate, a procedure or function. If it is declared local to the process it has access to that process's environment; otherwise you can pass it parameters. This is simplest when the subprogram can complete within the current clock cycle; often you can structure the code so that it can.

    Try the following in your synthesis tool:

    main_sm : process(clk)
    
       procedure wait_here (level : std_logic; nextstate : state_type) is
       begin
          subsystem_start <= level;
          if subsystem_busy = level then
             state <= nextstate;
          end if;
       end wait_here;
    
    begin
       ...
          when start_op =>
             wait_here('1', wait_subsystem);
          when wait_subsystem <= 
             wait_here('0', use_result);
    

    This rewrite of the handshaking above ought to work and in some synth tools it will, but others may not provide good synthesis support for subprograms.

    You can use subprograms spanning multiple clock cycles in processes in simulation; the trick is to eliminate the sensitivity list and use

    wait until rising_edge(clk);
    

    instead. This is also potentially synthesisable, and can be used e.g. in a loop in a procedure. However some synthesis tools reject it, and Xilinx XST for one is actually getting worse, rather than better, in support for it.