groovylight/sim/inc/devices.hpp

152 lines
3.9 KiB
C++
Raw Permalink Normal View History

// Project-specific cosimuluated devices.
#pragma once
#include "Vhub75e.h"
#include "tests.hpp"
// 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<unsigned char> row_array;
int xsize;
int ysize;
row_array row0{};
row_array row1{};
// the previous row values that were latched in.
std::vector<std::pair<row_array, row_array>> past_rows{};
// the pulse width for each output, in clock cycles.
std::vector<int> 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();
row1.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<int> &get_pulse_widths() { return this->pulse_widths; }
// return the RGB version.
std::pair<std::vector<unsigned int>, std::vector<unsigned int>> transpose() {
auto r0rgb = std::vector<unsigned int>(xsize, 0);
auto r1rgb = std::vector<unsigned int>(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);
}
};