Configurable architecture for multiple measurements thresholds detection

Andrzej Wojciechowski

Imagine you need to develop a module for FPGA (or ASIC) for measurements acquisition and thresholds detection. But some of the initial requirements are still unknown. How to design a module that could be easily configured for a given requirements?

Initial requirements

Suppose the project requirements are as follows:

  • measurements need to be read from multiple external ADC chips – the kind of chips (so the number of channels and the data width) and even the number of individual chips is not defined (and/or can change from project to project)
  • the module needs to detect if particular measurements are within a given limits
  • some of the limits are well known and should be hard-coded (to save resources), but some are required to be set during device operation
  • as the number of ADC chips (and kind of chips) is still not defined, it is also undefined which measurement should have an upper limit, lower limit, both or none.

The module can be used in different projects. Each project can use different type and number of ADC chips. So the designed module should be easily configurable to quickly adapt it for new requirements.

A naive approach

A first idea to implement the module might be to individually code each of the measurement acquisition and threshold detection.

A little better approach would be to use a for generate loop for measurements acquisition. This is probably the first thing that comes to mind in order to simplify the code and make it more configurable.
But as the requirements do not specify which threshold (high or low) should be implemented for which measurement, these are still individually coded.

In both cases one would end up with a code that is very long, repetitive, tedious to write (and update), and error-prone as well. In fact, it would be so tedious to write, I have no intention to do so and present here.

A configurable approach

Below I present a more configurable approach that can be easily adapted to modified requirements. In fact, it could be modified by any person (not necessarily a developer) with a few instructions, as it just requires a few constants be updated.

The example code below is written in VHDL, but a similar design can be written in Verilog as well.

architecture rtl of module is

constant C_ADC_W              : natural   := 10; -- with of ADC data
constant C_ADC_NUM            : natural   :=  5; -- number of ADC chips
constant C_ADC_CH_NUM         : natural   :=  8; -- number of channels per ADC chip

-- the types and constants definitions below can be moved to separate package
type TYPE_IMPL_THLD_ARRAY     is array(0 to C_ADC_CH_NUM*C_ADC_NUM-1) of boolean;
type TYPE_MEAS_ARRAY          is array(0 to C_ADC_NUM*C_ADC_CH_NUM-1) of std_logic_vector(C_ADC_W-1 downto 0);
type TYPE_THLD_ARRAY          is array(0 to C_ADC_CH_NUM*C_ADC_NUM-1) of integer;

-- array with information if a threshold should be implemented at all
constant C_IMPLEMENT_THLD_HI  : TYPE_IMPL_THLD_ARRAY := (
                                  2     => false,
                                 10     => false,
                                 13     => false,
                                 others => true);

constant C_IMPLEMENT_THLD_LO  : TYPE_IMPL_THLD_ARRAY := (
                                  1     => false,
                                  9     => false,
                                 10     => false,
                                 13     => false,
                                 others => true);

-- Hard-coded thresholds - negative value means no hard-coded value
constant C_HARDCODED_THLD_HI  : TYPE_THLD_ARRAY := (
                                  1 => 500,
                                  7 => 500,
                                 15 => 500,
                                 others => -1);
constant C_HARDCODED_THLD_LO  : TYPE_THLD_ARRAY := (
                                  2 => 200,
                                  7 => 200,
                                 12 => 200,
                                 others => -1);

signal measurement_array      : TYPE_MEAS_ARRAY; -- array signal storing all measurement values

signal thld_hi_received       : TYPE_MEAS_ARRAY; -- high threshold value et during operation
signal thld_lo_received       : TYPE_MEAS_ARRAY; --  low threshold value et during operation

signal thld_hi                : TYPE_MEAS_ARRAY; -- measurement threshold high
signal thld_lo                : TYPE_MEAS_ARRAY; -- measurement threshold low

begin

GEN_ADC_ACQ : for gi in 0 to C_ADC_NUM-1 generate
   ...
   -- the specific ADC interface to store each measurement in a corresponding "measurement_array" element
end generate GEN_ADC_ACQ;


GEN_HK_THLD : for gi in 0 to C_ADC_NUM*C_ADC_CH_NUM-1 generate
   GEN_IMPLEMENT_HI_THLD: if C_IMPLEMENT_THLD_HI(gi) generate
      GEN_HARDCODE_HI_THLD: if C_HARDCODED_THLD_HI(gi) >= 0 generate
         -- hard-coded high threshold value from constant
         thld_hi(gi)          <= std_logic_vector(to_unsigned(C_HARDCODED_THLD_HI(gi), C_ADC_W));
      end generate GEN_HARDCODE_HI_THLD;
      GEN_NOT_HARDCODE_HI_THLD: if C_HARDCODED_THLD_HI(gi) < 0 generate
         -- high threshold value defined during operation
         thld_hi(gi)          <= thld_hi_received(gi);
      end generate GEN_NOT_HARDCODE_HI_THLD;
      -- a place for actual threshold detection
      ...
   end generate GEN_IMPLEMENT_HI_THLD;

   GEN_IMPLEMENT_LO_THLD: if C_IMPLEMENT_THLD_LO(gi) generate
      GEN_HARDCODE_LO_THLD: if C_HARDCODED_THLD_LO(gi) >= 0 generate
         -- hard-coded high threshold value from constant
         thld_lo(gi)          <= std_logic_vector(to_unsigned(C_HARDCODED_THLD_LO(gi), C_ADC_W));
      end generate GEN_HARDCODE_LO_THLD;
      GEN_NOT_HARDCODE_LO_THLD: if C_HARDCODED_THLD_LO(gi) < 0 generate
         -- low threshold value defined during operation
         thld_lo(gi)          <= thld_lo_received(gi);
      end generate GEN_NOT_HARDCODE_LO_THLD;
      -- a place for actual threshold detection
      ...
   end generate GEN_IMPLEMENT_LO_THLD;
end generate GEN_HK_THLD;

end rtl;

The first three constants (C_ADC_W, C_ADC_NUM, C_ADC_CH_NUM) define the number of ADC chips and their parameters.
The rest of the code is based on the array indexes approach. Each measurement is stored at the same index as the corresponding thresholds (and constants) in a different array. This way a multiple for generate and if generate blocks can be used instead of individually coding functionality for each measurement.

The C_IMPLEMENT_THLD_HI and C_IMPLEMENT_THLD_LO constants define if a given measurement should be implemented, or not.

The C_HARDCODED_THLD_HI and C_HARDCODED_THLD_LO constants define a hard-coded thresholds values. If the value is negative, a threshold is not hard-coded and can be defined during device operation. The same constants can be used i.e. to decide if a given bus register (storing the threshold) is implemented or not.

Hopefully the presented approach will be helpful in future designs.

1 thought on “Configurable architecture for multiple measurements thresholds detection”

Comments are closed.