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!
This commit is contained in:
David Anderson 2024-08-13 20:53:47 -07:00
parent dad128b56b
commit e6fa717507
4 changed files with 316 additions and 0 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;