~shiny/gbcap

d95edc0c1b795e1ca64e9635aec5112192ce18cf — Thomas Spurden 3 years ago 255dccd
Testbench for gbcap lines->compress->uart
2 files changed, 143 insertions(+), 19 deletions(-)

M gbcap.py
A gbcap_tb_wrap.cpp
M gbcap.py => gbcap.py +59 -19
@@ 100,12 100,13 @@ class PixelStream(Module):
        ]

class LCDLineInput(Module):
    def __init__(self, lcd, capture, ddr_inp):
    def __init__(self, lcd, ddr_inp):
        self.valid = Signal(name='line_valid')
        # rightmost pixel in MSB, leftmost pixel in LSB
        self.line = Signal(LINE_BITS, name='line_shiftreg')
        self.frame_done = Signal()

        self.start_capture = Signal(reset=0)
        self.capturing = Signal(reset=0)

        self.submodules.pixels = PixelStream(lcd, ddr_inp)


@@ 114,7 115,7 @@ class LCDLineInput(Module):

        self.sync += [
            # Only start/stop capturing at the start of a frame
            If(self.pixels.frame_start, self.capturing.eq(capture)),
            If(self.pixels.frame_start, self.capturing.eq(self.start_capture)),
            self.frame_done.eq(self.pixels.frame_start & self.capturing)
        ]



@@ 129,41 130,39 @@ class LCDLineInput(Module):
            )
        ]

class PixelsToUART(Module):
    def __init__(self, lcd, serial, clk_period_ns, baud_rate, ddr_inp):
        self.capture = Signal(reset=0)

        self.submodules.lcd = LCDLineInput(lcd, self.capture, ddr_inp)
        self.submodules.fifo = SyncFIFOBuffered(8, LINE_BYTES + 1)

        self.submodules.uart = uart.RS232PHY(serial, clk_period_ns, baud_rate)
        self.frame_done = self.lcd.frame_done

class CompressToUART(Module):
    def __init__(self, line_data, line_valid, serial, clk_period_ns, baud_rate):
        self.capture_req = Signal(reset=0)
        self.error = Signal()

        # lines --> compressor --> fifo --> UART
        # output FIFO can buffer a whole uncompressed line so the compression of
        # the next line should always be able to overlap the output of a line
        self.submodules.compress = compress.CompressFrames(LINE_PX, FRAME_HEIGHT, 1024)
        self.submodules.fifo = SyncFIFOBuffered(8, LINE_BYTES + 1)
        self.submodules.uart = uart.RS232PHY(serial, clk_period_ns, baud_rate)

        ###

        self.comb += [
            self.compress.input.data.eq(self.lcd.line),
            self.compress.input.valid.eq(self.lcd.valid),
            self.compress.input.data.eq(line_data),
            self.compress.input.valid.eq(line_valid),
            self.fifo.din.eq(self.compress.output.data),
            self.fifo.we.eq(self.compress.output.valid),
            self.compress.output.ready.eq(self.fifo.writable),
        ]

        self.sync += [
            If(self.lcd.valid & ~self.compress.input.ready, self.error.eq(1)),
            If(line_valid & ~self.compress.input.ready, self.error.eq(1)),
        ]

        self.sync += [
            # Start capture on frame start after UART rx
            If(self.uart.rx.valid, self.capture.eq(1)),
            If(self.uart.rx.valid, self.capture_req.eq(1)),
            # Stop on error (fifo overflow), and clear the error so it is
            # possible to start caturing again
            If(self.error,
                self.capture.eq(0),
                self.capture_req.eq(0),
                self.error.eq(0)),
        ]



@@ 173,6 172,19 @@ class PixelsToUART(Module):
            self.uart.tx.data.eq(self.fifo.dout)
        ]

class PixelsToUART(Module):
    def __init__(self, lcd, serial, clk_period_ns, baud_rate, ddr_inp):
        self.submodules.lcd = LCDLineInput(lcd, ddr_inp)
        self.submodules.comp_out = CompressToUART(self.lcd.line, self.lcd.valid,
            serial, clk_period_ns, baud_rate)

        self.frame_done = self.lcd.frame_done
        self.error = self.comp_out.error

        ###

        self.comb += self.lcd.start_capture.eq(self.comp_out.capture_req)

if __name__ == '__main__':
    import sys
    if sys.argv[1] == 'build':


@@ 217,7 229,7 @@ if __name__ == '__main__':
            yield dut.loopback.tx.valid.eq(0)

            # Wait for capture to start (byte received)
            while (yield dut.pixel_uart.capture) == 0:
            while (yield dut.pixel_uart.lcd.start_capture) == 0:
                yield

            yield from gen(dut)


@@ 228,7 240,7 @@ if __name__ == '__main__':
            codec = compress_ref.CODEC(LINE_PX, FRAME_HEIGHT, 4)
            compressed = list(codec.compress(lines))

            output = dut.pixel_uart.compress.output
            output = dut.pixel_uart.comp_out.compress.output
            for i, b in enumerate(compressed):
                while (yield output.valid) == 0 or (yield output.ready) == 0:
                    yield


@@ 272,3 284,31 @@ if __name__ == '__main__':

        dut = DUT()
        run_simulation(dut, [tx(dut), rx(dut), check_comp_out(dut)], vcd_name='out.vcd')

    elif sys.argv[1] == 'verilator':
        import utils
        from migen.fhdl.verilog import convert
        class TB(Module):
            def __init__(self):
                self.serial_pads = Record([('tx', 1), ('rx', 1)])
                self.loop_serial_pads = Record([('tx', 1), ('rx', 1)])
                self.line_pads = Record([('data', 320), ('valid', 1)])
                self.submodules.compress = CompressToUART(
                    self.line_pads.data, self.line_pads.valid, self.serial_pads,
                    31.25, baud_rate=2000000)
                self.submodules.loopback = uart.RS232PHY(self.loop_serial_pads, 31.25, baud_rate=2000000)

                self.comb += [
                    self.serial_pads.rx.eq(self.loop_serial_pads.tx),
                    self.loop_serial_pads.rx.eq(self.serial_pads.tx)
                ]

        tb = TB()
        ios = {
            tb.line_pads.data, tb.line_pads.valid,
            tb.loopback.tx.data, tb.loopback.tx.valid,
            tb.loopback.rx.data, tb.loopback.rx.valid,
        }
        c = convert(tb, ios=ios)
        utils.update_verilog_if_changed('gbcap_tb.v', c)
        utils.verilate(['gbcap_tb.v', 'gbcap_tb_wrap.cpp'], output='gbcap_tb')

A gbcap_tb_wrap.cpp => gbcap_tb_wrap.cpp +84 -0
@@ 0,0 1,84 @@
#include "Vgbcap_tb.h"
#include "verilated.h"
#include "verilated_vcd_c.h"
#include "tb_wrap_utils.h"

#include <stdio.h>

int main(int argc, char **argv)
{
	Verilated::commandArgs(argc, argv);
	Vgbcap_tb* top = new Vgbcap_tb;

	Verilated::traceEverOn(true);
	VerilatedVcdC* trace = new VerilatedVcdC;
	top->trace(trace, 99);

	trace->open("gbcap_tb.vcd");

	top->sys_rst = 1;
	for(unsigned i = 0; i != 10; i += 1) {
		top->sys_clk = i & 1;
		top->eval();
	}
	top->sys_rst = 0;

	top->valid = 1;
	top->data = 0xBD;

	uint32_t last_line = UINT32_MAX;
	uint32_t sys_clk = 0;
	bool no_more_lines = false;
	while(!Verilated::gotFinish()) {
		/* GB LCD runs at ~4.19MHz, each scanline takes 456 cycles. FPGA clock
		 * is 25MHz, so divide by 6 to get 4MHz.
		 */
		uint32_t line_idx = sys_clk / (6 * 456);
		/* GB LCD frame is 144 lines high, with 10 lines worth of vblank at the
		 * end of each frame.
		 */
		uint32_t line_in_frame = line_idx % 154;

		if(!no_more_lines && line_idx != last_line) {
			last_line = line_idx;
			fprintf(stderr, "input: line %d %d (%u)\n", line_idx, line_in_frame, sys_clk);
			if(line_in_frame < 144) {
				if(!read_line(top->line_pads_data)) {
					no_more_lines = true;
					fprintf(stderr, "**EOF\n");
				}
				else {
					top->line_pads_valid = 1;
				}
			}
		}

		/* Falling edge */
		top->sys_clk = 0;
		top->eval();
		trace->dump(sys_clk << 1);
		/* Rising edge */
		top->sys_clk = 1;
		top->eval();
		trace->dump((sys_clk << 1) + 1);

		if(top->line_pads_valid) {
			top->line_pads_valid = 0;
		}

		if(top->valid_1) {
			printf("%02x\n", top->data_1);
			fflush(stdout);
		}

		if(top->valid) {
			top->valid = 0;
		}

		sys_clk += 1;
	}

	trace->close();

	delete top;
}