// Project-specific cosimuluated devices. #pragma once #include "tests.hpp" #include "Vhub75e.h" // slices the RGB values for us. uint8_t rgb_slice(uint32_t rgb, uint8_t bit) { if (bit > 8) { // todo: panic return 0; } uint8_t r = (rgb >> (16 + bit)) & 1; uint8_t g = (rgb >> (8 + bit)) & 1; uint8_t b = (rgb >> bit) & 1; return (r << 2) & (g << 1) & (b << 1); } void rgb_unslice(unsigned int &rgb, uint8_t bits, uint8_t bitpos) { if (bitpos > 7 || bits > 0b111) { // TODO: panic. return; } auto r = (bits >> 2) & 1; auto g = (bits >> 1) & 1; auto b = (bits >> 0) & 1; rgb |= r << bitpos << 16; rgb |= g << bitpos << 8; rgb |= b << bitpos << 0; } class HUB75Reciever : public CosimulatedDevice { typedef std::vector row_array; int xsize; int ysize; row_array row0{}; row_array row1{}; // the previous row values that were latched in. std::vector> past_rows{}; // the pulse width for each output, in clock cycles. std::vector pulse_widths{}; int bit_position = 7; // the bit that is currently being shifted in int output_period_cnt; // if oe = 0, count clocks. when oe = 1, store value into // pulse_widths[display_bit]; // previous latch value, used to identify when to latch. unsigned char prev_latch = 0; // previous display clock value, used to detect rising edge. unsigned char prev_display_clk = 0; unsigned char prev_clk = 0; unsigned char prev_oe = 1; // assuming starting high. // references to the panel driver signals. VL_IN8(&display_clk, 0, 0); VL_IN8(&out_enable, 0, 0); VL_IN8(&latch, 0, 0); VL_IN8(&rgb0, 2, 0); VL_IN8(&rgb1, 2, 0); VL_IN8(&clk, 0, 0); public: HUB75Reciever(int xsize, int ysize, const Vhub75e &dut) : clk(dut.clk), display_clk(dut.display_clk), out_enable(dut.out_enable), latch(dut.latch), rgb0(dut.panel_rgb0), rgb1(dut.panel_rgb1) { this->xsize = xsize; this->ysize = ysize; row0.clear(); prev_oe = out_enable; prev_display_clk = display_clk; prev_latch = latch; prev_clk = clk; }; // evaluates the reciever. virtual void tick() override { if (prev_display_clk == 0 && display_clk == 1) { // display clock rising edge. row0.push_back(rgb0); row1.push_back(rgb1); } if (prev_latch == 0 && latch == 1) { // latch in the data: reverse the rows, and pu std::reverse(row0.begin(), row0.end()); std::reverse(row1.begin(), row1.end()); past_rows.push_back(std::pair(row0, row1)); row0.clear(); row1.clear(); } if (prev_clk == 0 && clk == 1) { if (out_enable == 0) { if (prev_oe == 1) { // falling edge. output_period_cnt = 1; } else { output_period_cnt++; } } else { // out_enable == 1 if (prev_oe == 1) { // do nothing } if (prev_oe == 0) { // rising edge pulse_widths.push_back(output_period_cnt); } } } // update previous values prev_display_clk = display_clk; prev_latch = latch; prev_oe = out_enable; prev_clk = clk; } const auto &get_past_rows() { return this->past_rows; } const std::vector &get_pulse_widths() { return this->pulse_widths; } // return the RGB version. std::pair, std::vector> transpose() { auto r0rgb = std::vector(xsize, 0); auto r1rgb = std::vector(xsize, 0); auto bitdepth = pulse_widths.size(); // TODO: use more sophisticated slicing. auto slice = bitdepth - 1; for (const auto &[row0slice, row1slice] : this->past_rows) { for (int i = 0; i < row0slice.size(); i++) { rgb_unslice(r0rgb[i], row0slice[i], slice); rgb_unslice(r1rgb[i], row1slice[i], slice); } slice--; } return std::pair(r0rgb, r1rgb); } };