From e6fa7175078aed5ebfe2989eebba3e7551a4c647 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 13 Aug 2024 20:53:47 -0700 Subject: [PATCH] Experiment comparing bsc-contrib's video timing generator with brute force Brute force is a naively written state machine that combines both horizontal and vertical timings into one, in a way that unrolls comically badly. It's obviously uncompetitive as-is, but I wanted to use that as a starting point to see how much bsc and yosys would still be able to cope with it. The result: the worse code takes much longer for bluespec to evaluate, and it consumes ~4x the amount of logic elements after synthesis. Less terrible than I expected, to be honest! --- experiments/timing_generator/contrib/Top.bsv | 152 ++++++++++++++++++ .../timing_generator/contrib/pin_map.lpf | 28 ++++ experiments/timing_generator/mine/Top.bsv | 108 +++++++++++++ experiments/timing_generator/mine/pin_map.lpf | 28 ++++ 4 files changed, 316 insertions(+) create mode 100644 experiments/timing_generator/contrib/Top.bsv create mode 100644 experiments/timing_generator/contrib/pin_map.lpf create mode 100644 experiments/timing_generator/mine/Top.bsv create mode 100644 experiments/timing_generator/mine/pin_map.lpf diff --git a/experiments/timing_generator/contrib/Top.bsv b/experiments/timing_generator/contrib/Top.bsv new file mode 100644 index 0000000..9118cbb --- /dev/null +++ b/experiments/timing_generator/contrib/Top.bsv @@ -0,0 +1,152 @@ +// This code is derived from the video sync generator at +// https://github.com/B-Lang-org/bsc-contrib/blob/main/Libraries/FPGA/Misc/Video.bsv, +// simplified down to serve as an experiment/comparison of synthesis +// efficiency with the sibling directory that has a more imperative +// construction (and also worse, it tursn out). + +package Top; + +import Counter::*; +import StmtFSM::*; + +typedef struct { + Integer active; + Integer fporch; + Integer sync; + Integer bporch; +} SyncDescriptor; + +SyncDescriptor horizontal = SyncDescriptor{ + active: 640, + fporch: 16, + sync: 96, + bporch: 48 +}; + +SyncDescriptor vertical = SyncDescriptor{ + active: 480, + fporch: 10, + sync: 2, + bporch: 33 +}; + +interface SyncGenerator; + method Action tick(); + method Bool preedge(); + method Bool out_n(); + method Bool out(); + method Bool active(); +endinterface + +module mkSyncGenerator#(SyncDescriptor info)(SyncGenerator); + let maxActive = fromInteger(info.active - 1); + let maxFPorch = fromInteger(info.fporch - 1); + let maxSync = fromInteger(info.sync - 1); + let maxBPorch = fromInteger(info.bporch - 1); + + ///////////////////// + /// Design Elements + ///////////////////// + Counter#(16) rCounter <- mkCounter(0); + + PulseWire pwTick <- mkPulseWire; + PulseWire pwPreSyncEdge <- mkPulseWire; + Reg#(Bool) rSyncOut <- mkReg(True); + Reg#(Bool) rActive <- mkReg(False); + + ///////////////////// + /// Rules + ///////////////////// + Stmt machine = + seq + while(True) seq + // Front Porch + while(rCounter.value < maxFPorch) action + rCounter.up; + endaction + + action + rCounter.clear; + pwPreSyncEdge.send; + rSyncOut <= False; + rActive <= False; + endaction + + // Sync Pulse + while(rCounter.value < maxSync) action + rCounter.up; + endaction + + action + rCounter.clear; + rSyncOut <= True; + rActive <= False; + endaction + + // Back Porch + while(rCounter.value < maxBPorch) action + rCounter.up; + endaction + + action + rCounter.clear; + rSyncOut <= True; + rActive <= True; + endaction + + // Active + while(rCounter.value < maxActive) action + rCounter.up; + endaction + + action + rCounter.clear; + rSyncOut <= True; + rActive <= False; + endaction + endseq + endseq; + + FSM fsmSyncGen <- mkFSMWithPred(machine, pwTick); + + rule start_sync_generator(fsmSyncGen.done); + fsmSyncGen.start; + endrule + + method Action tick = pwTick.send; + method Bool preedge = pwPreSyncEdge; + method Bool out_n = rSyncOut; + method Bool out = !rSyncOut; + method Bool active = rActive; +endmodule: mkSyncGenerator + +interface ITop; + (* always_ready *) + method Bool paint(); + (* always_ready *) + method Bool hsync(); + (* always_ready *) + method Bool vsync(); +endinterface + +(* synthesize *) +module mkTop (ITop); + let horiz <- mkSyncGenerator(horizontal); + let vert <- mkSyncGenerator(vertical); + + (* no_implicit_conditions, fire_when_enabled *) + rule advance_horizontal; + horiz.tick; + endrule + + (* no_implicit_conditions, fire_when_enabled *) + rule advance_vertical (horiz.preedge); + vert.tick; + endrule + + method Bool paint = horiz.active && vert.active; + method Bool hsync = horiz.out; + method Bool vsync = vert.out; +endmodule + +endpackage diff --git a/experiments/timing_generator/contrib/pin_map.lpf b/experiments/timing_generator/contrib/pin_map.lpf new file mode 100644 index 0000000..aac1a01 --- /dev/null +++ b/experiments/timing_generator/contrib/pin_map.lpf @@ -0,0 +1,28 @@ +BLOCK RESETPATHS; +BLOCK ASYNCPATHS; + +SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 MASTER_SPI_PORT=ENABLE SLAVE_SPI_PORT=DISABLE SLAVE_PARALLEL_PORT=DISABLE; + +LOCATE COMP "CLK" SITE "G2"; +IOBUF PORT "CLK" PULLMODE=NONE IO_TYPE=LVCMOS33; +FREQUENCY PORT "CLK" 25 MHZ; + +## LED indicators "blinkey" and "gpio" sheet +LOCATE COMP "paint" SITE "H3"; +LOCATE COMP "hsync" SITE "E1"; +LOCATE COMP "vsync" SITE "E2"; +IOBUF PORT "paint" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; +IOBUF PORT "hsync" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; +IOBUF PORT "vsync" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; + +LOCATE COMP "RST_N" SITE "D6"; # BTN_PWRn (inverted logic) +IOBUF PORT "RST_N" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; + +LOCATE COMP "uart_rx_v" SITE "M1"; # FIRE1 +IOBUF PORT "uart_rx_v" PULLMODE=UP IO_TYPE=LVCMOS33; + +LOCATE COMP "uart_tx" SITE "L4"; # FPGA transmits to ftdi +IOBUF PORT "uart_tx" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; + +LOCATE COMP "wifi_gpio0" SITE "L2"; +IOBUF PORT "wifi_gpio0" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; diff --git a/experiments/timing_generator/mine/Top.bsv b/experiments/timing_generator/mine/Top.bsv new file mode 100644 index 0000000..5a5918c --- /dev/null +++ b/experiments/timing_generator/mine/Top.bsv @@ -0,0 +1,108 @@ +package Top; + +import Counter::*; +import StmtFSM::*; + +typedef struct { + Integer active; + Integer fporch; + Integer sync; + Integer bporch; +} SyncDescriptor; + +SyncDescriptor horizontal = SyncDescriptor{ + active: 640, + fporch: 16, + sync: 96, + bporch: 48 +}; + +SyncDescriptor vertical = SyncDescriptor{ + active: 480, + fporch: 10, + sync: 2, + bporch: 33 +}; + +interface SyncGenerator; + method Action tick(); + method Bool preedge(); + method Bool out_n(); + method Bool out(); + method Bool active(); +endinterface + +interface ITop; + (* always_ready *) + method Bool paint(); + (* always_ready *) + method Bool hsync(); + (* always_ready *) + method Bool vsync(); +endinterface + +(* synthesize *) +module mkTop (ITop); + Reg#(Bool) rPaint <- mkReg(False); + Reg#(Bool) rHsync <- mkReg(False); + Reg#(Bool) rVsync <- mkReg(False); + + let blank_line_fsm = seq + // Sync pulse + repeat (96) rHsync <= True; + + // Back porch + repeat (48) rHsync <= False; + + // Visible area + repeat (640) noAction; + + // Front porch + repeat (16) noAction; + endseq; + let video_line_fsm = seq + // Sync pulse + repeat (96) rHsync <= True; + + // Back porch + repeat (48) rHsync <= False; + + // Visible area + repeat (640) rPaint <= True; + + // Front porch + repeat (16) rPaint <= False; + endseq; + let frame_fsm = seq + while (True) seq + // Sync pulse + repeat (2) par + rVsync <= True; + blank_line_fsm; + endpar + + // Back porch + repeat (33) par + rVsync <= False; + blank_line_fsm; + endpar + + // Visible area + repeat (480) video_line_fsm; + + // Front porch + repeat (10) blank_line_fsm; + endseq + endseq; + let fsm <- mkFSM(frame_fsm); + + rule run_timing (fsm.done()); + fsm.start(); + endrule + + method Bool paint = rPaint._read; + method Bool hsync = rHsync._read; + method Bool vsync = rVsync._read; +endmodule + +endpackage diff --git a/experiments/timing_generator/mine/pin_map.lpf b/experiments/timing_generator/mine/pin_map.lpf new file mode 100644 index 0000000..aac1a01 --- /dev/null +++ b/experiments/timing_generator/mine/pin_map.lpf @@ -0,0 +1,28 @@ +BLOCK RESETPATHS; +BLOCK ASYNCPATHS; + +SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 MASTER_SPI_PORT=ENABLE SLAVE_SPI_PORT=DISABLE SLAVE_PARALLEL_PORT=DISABLE; + +LOCATE COMP "CLK" SITE "G2"; +IOBUF PORT "CLK" PULLMODE=NONE IO_TYPE=LVCMOS33; +FREQUENCY PORT "CLK" 25 MHZ; + +## LED indicators "blinkey" and "gpio" sheet +LOCATE COMP "paint" SITE "H3"; +LOCATE COMP "hsync" SITE "E1"; +LOCATE COMP "vsync" SITE "E2"; +IOBUF PORT "paint" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; +IOBUF PORT "hsync" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; +IOBUF PORT "vsync" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; + +LOCATE COMP "RST_N" SITE "D6"; # BTN_PWRn (inverted logic) +IOBUF PORT "RST_N" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; + +LOCATE COMP "uart_rx_v" SITE "M1"; # FIRE1 +IOBUF PORT "uart_rx_v" PULLMODE=UP IO_TYPE=LVCMOS33; + +LOCATE COMP "uart_tx" SITE "L4"; # FPGA transmits to ftdi +IOBUF PORT "uart_tx" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; + +LOCATE COMP "wifi_gpio0" SITE "L2"; +IOBUF PORT "wifi_gpio0" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;