// Copyright (c) maehw, 2022
// wokwi-lookup-table-generator is licensed under the GNU General Public License v3.0
// Copyright and license notices must be preserved. Contributors provide an express grant of patent rights.

/* Let's start with design specific configuration */

#define DESIGN_NUM_USED_INPUTS      (4u)
#define DESIGN_NUM_USED_OUTPUTS     (7u)

/* Define whether the verification shall halt on the detection of the
 * first error (true) or keep on running to the end (false).
 */
#define VERIFICATION_STOP_ON_ERROR  (true)

/* This table shall be generated from the truth table!
 * Obviously, also design specific.
 */
uint16_t expected_out_val[2 << (DESIGN_NUM_USED_INPUTS-1)] = {
    0b1111110,
    0b0110000,
    0b1101101,
    0b1111001,
    0b0110011,
    0b1011011,
    0b0011111,
    0b1110000,
    0b1101111,
    0b1110011,
    0b0000000,
    0b0000000,
    0b0000000,
    0b0000000,
    0b0000000,
    0b0000000,
};

/* Option to pretty print the input value,
 * dependent on your design.
 */
#undef VERIFICATION_PRETTY_PRINT_INPUT_VAL

#undef VERIFICATION_PRETTY_PRINT_EXPECTED_OUT_VAL

#undef VERIFICATION_PRETTY_PRINT_REAL_OUT_VAL

// ------------------------------------------------------------------------------
/* Some parameters to tune verification, but actually
 * no real need to touch any code below this line
 */

/* As this is run in simulation, baud rate is not that important */
#define SERIAL_BAUDRATE             (230400u)

/* These params can be used to control simulation speed;
 * getting a correct reading the 7-segment display on my device takes quite some time.
 */
#define VERIFICATION_SETUP_TIME_MS  (50u)
#define VERIFICATION_HOLD_TIME_MS   (350u)

// ------------------------------------------------------------------------------
/* No real need to touch any code below this line */

/* The mapping of 10 input and 10 output pins each could be static;
 * however, this limits support to designs with <= 10 input and <= 10 output pins,
 * instead of limiting the support to designs with an overall pin count of 20!
 */
#define DESIGN_IN_0   ( 2)
#define DESIGN_IN_1   ( 3)
#define DESIGN_IN_2   ( 4)
#define DESIGN_IN_3   ( 5)
#define DESIGN_IN_4   ( 6)
#define DESIGN_IN_5   ( 7)
#define DESIGN_IN_6   ( 8)
#define DESIGN_IN_7   ( 9)
#define DESIGN_IN_8   (10)
#define DESIGN_IN_9   (11)

#define DESIGN_OUT_0  (12)
#define DESIGN_OUT_1  (13)
#define DESIGN_OUT_2  (14)
#define DESIGN_OUT_3  (15)
#define DESIGN_OUT_4  (16)
#define DESIGN_OUT_5  (17)
#define DESIGN_OUT_6  (18)
#define DESIGN_OUT_7  (19)
#define DESIGN_OUT_8  (20)
#define DESIGN_OUT_9  (21)

void setup()
{
  const bool stop_verification_on_error = VERIFICATION_STOP_ON_ERROR;
  bool tests_passed = true; /* asume the best for the start */

  Serial.begin(SERIAL_BAUDRATE);

  pinMode(DESIGN_IN_0, OUTPUT);
  pinMode(DESIGN_IN_1, OUTPUT);
  pinMode(DESIGN_IN_2, OUTPUT);
  pinMode(DESIGN_IN_3, OUTPUT);
  pinMode(DESIGN_IN_4, OUTPUT);
  pinMode(DESIGN_IN_5, OUTPUT);
  pinMode(DESIGN_IN_6, OUTPUT);
  pinMode(DESIGN_IN_7, OUTPUT);
  pinMode(DESIGN_IN_8, OUTPUT);
  pinMode(DESIGN_IN_9, OUTPUT);

  pinMode(DESIGN_OUT_0, INPUT);
  pinMode(DESIGN_OUT_1, INPUT);
  pinMode(DESIGN_OUT_2, INPUT);
  pinMode(DESIGN_OUT_3, INPUT);
  pinMode(DESIGN_OUT_4, INPUT);
  pinMode(DESIGN_OUT_5, INPUT);
  pinMode(DESIGN_OUT_6, INPUT);
  pinMode(DESIGN_OUT_7, INPUT);
  pinMode(DESIGN_OUT_8, INPUT);
  pinMode(DESIGN_OUT_9, INPUT);

  Serial.print("Design has ");
  Serial.print(DESIGN_NUM_USED_INPUTS, DEC);
  Serial.println(" inputs.");

  Serial.print("Design has ");
  Serial.print(DESIGN_NUM_USED_OUTPUTS, DEC);
  Serial.println(" outputs.");

  Serial.print("Testing all 2^");
  Serial.print(DESIGN_NUM_USED_INPUTS, DEC);
  Serial.println(" input combinations.");

  Serial.print("Stop verification on error? ");
  Serial.println(stop_verification_on_error ? "Yes" : "No");

  for(uint16_t in_val = 0; ( in_val < (2 << (DESIGN_NUM_USED_INPUTS-1)) ) &&
                            ( !stop_verification_on_error ||
                              (stop_verification_on_error && tests_passed) ); in_val++ )
  {
    set_design_input_val(in_val);

    // wait some time before checking outputs
    delay(VERIFICATION_SETUP_TIME_MS);

    tests_passed &= verify_design_output_val(in_val);

    // wait some time before setting next inputs
    delay(VERIFICATION_HOLD_TIME_MS);
  }

  Serial.println();
  if(tests_passed)
  {
    Serial.println("[PASSED]");
  }
  else
  {
    Serial.println("[FAILED]");
  }
}

void set_design_input_val(uint16_t val)
{
  /* Set logic design inputs at Arduino's output pins;
   * output the bits via serial later, so that we don't add large delay between the different pins!
   */
  if( (DESIGN_NUM_USED_INPUTS-1) >= 0 )
  {
    digitalWrite(DESIGN_IN_0, val & (1 << DESIGN_NUM_USED_INPUTS-1));
  }
  if( (DESIGN_NUM_USED_INPUTS-2) >= 0 )
  {
    digitalWrite(DESIGN_IN_1, val & (1 << DESIGN_NUM_USED_INPUTS-2));
  }
  if( (DESIGN_NUM_USED_INPUTS-3) >= 0 )
  {
    digitalWrite(DESIGN_IN_2, val & (1 << DESIGN_NUM_USED_INPUTS-3));
  }
  if( (DESIGN_NUM_USED_INPUTS-4) >= 0 )
  {
    digitalWrite(DESIGN_IN_3, val & (1 << DESIGN_NUM_USED_INPUTS-4));
  }
  if( (DESIGN_NUM_USED_INPUTS-5) >= 0 )
  {
    digitalWrite(DESIGN_IN_4, val & (1 << DESIGN_NUM_USED_INPUTS-5));
  }
  if( (DESIGN_NUM_USED_INPUTS-6) >= 0 )
  {
    digitalWrite(DESIGN_IN_5, val & (1 << DESIGN_NUM_USED_INPUTS-6));
  }
  if( (DESIGN_NUM_USED_INPUTS-7) >= 0 )
  {
    digitalWrite(DESIGN_IN_6, val & (1 << DESIGN_NUM_USED_INPUTS-7));
  }
  if( (DESIGN_NUM_USED_INPUTS-8) >= 0 )
  {
    digitalWrite(DESIGN_IN_7, val & (1 << DESIGN_NUM_USED_INPUTS-8));
  }
  if( (DESIGN_NUM_USED_INPUTS-9) >= 0 )
  {
    digitalWrite(DESIGN_IN_8, val & (1 << DESIGN_NUM_USED_INPUTS-9));
  }
  if( (DESIGN_NUM_USED_INPUTS-10) >= 0 )
  {
    digitalWrite(DESIGN_IN_9, val & (1 << DESIGN_NUM_USED_INPUTS-10));
  }

  Serial.print("\nWrote input: 0b");
  for(int8_t bit_index = DESIGN_NUM_USED_INPUTS-1; bit_index >= 0; bit_index--)
  {
    Serial.print((val >> bit_index) & 1, BIN);
  }
#ifdef VERIFICATION_PRETTY_PRINT_INPUT_VAL
  VERIFICATION_PRETTY_PRINT_INPUT_VAL(val);
#endif
  Serial.println();
}

bool verify_design_output_val(uint16_t in_val)
{
  // read value from logic design outputs at Arduino's input pins
  uint16_t val = 0;
  if( DESIGN_NUM_USED_OUTPUTS >= 1 )
  {
    val |= digitalRead(DESIGN_OUT_0);
    val <<= 1;
  }
  if( DESIGN_NUM_USED_OUTPUTS >= 2 )
  {
    val |= digitalRead(DESIGN_OUT_1);
    val <<= 1;
  }
  if( DESIGN_NUM_USED_OUTPUTS >= 3 )
  {
    val |= digitalRead(DESIGN_OUT_2);
    val <<= 1;
  }
  if( DESIGN_NUM_USED_OUTPUTS >= 4 )
  {
    val |= digitalRead(DESIGN_OUT_3);
    val <<= 1;
  }
  if( DESIGN_NUM_USED_OUTPUTS >= 5 )
  {
    val |= digitalRead(DESIGN_OUT_4);
    val <<= 1;
  }
  if( DESIGN_NUM_USED_OUTPUTS >= 6 )
  {
    val |= digitalRead(DESIGN_OUT_5);
    val <<= 1;
  }
  if( DESIGN_NUM_USED_OUTPUTS >= 7 )
  {
    val |= digitalRead(DESIGN_OUT_6);
    val <<= 1;
  }
  if( DESIGN_NUM_USED_OUTPUTS >= 8 )
  {
    val |= digitalRead(DESIGN_OUT_7);
    val <<= 1;
  }
  if( DESIGN_NUM_USED_OUTPUTS >= 9 )
  {
    val |= digitalRead(DESIGN_OUT_8);
    val <<= 1;
  }
  if( DESIGN_NUM_USED_OUTPUTS >= 10 )
  {
    val |= digitalRead(DESIGN_OUT_9);
    val <<= 1;
  }
  /* the last shift is always one too many */
  val >>= 1;


  Serial.print("  Expected output:  0b");
  for(int8_t bit_index = DESIGN_NUM_USED_OUTPUTS-1; bit_index >= 0; bit_index--)
  {
    Serial.print((expected_out_val[in_val] >> bit_index) & 1, BIN);
  }
#ifdef VERIFICATION_PRETTY_PRINT_EXPECTED_OUT_VAL
  VERIFICATION_PRETTY_PRINT_EXPECTED_OUT_VAL(expected_out_val[in_val]);
#endif
  Serial.println();

  Serial.print("  Read back output: 0b");
  for(int8_t bit_index = DESIGN_NUM_USED_OUTPUTS-1; bit_index >= 0; bit_index--)
  {
    Serial.print((val >> bit_index) & 1, BIN);
  }
#ifdef VERIFICATION_PRETTY_PRINT_REAL_OUT_VAL
  VERIFICATION_PRETTY_PRINT_REAL_OUT_VAL();
#endif
  Serial.println();

  if(expected_out_val[in_val] == val)
  {
    Serial.println("  [PASS]");
    return true;
  }
  else
  {
    Serial.println("  [FAIL]");
    return false;
  }
}

void loop()
{
  /* no need to loop as everything is only required once and therefore
   * already done in setup()
   */
}