diff --git a/.github/workflows/linux-test.yml b/.github/workflows/linux-test.yml index a2ede8600..7f5c022e5 100644 --- a/.github/workflows/linux-test.yml +++ b/.github/workflows/linux-test.yml @@ -55,6 +55,13 @@ jobs: pytest --pycodestyle tests/ . --ignore=riscv_mini cd - + - name: Test icebreaker + run: | + cd examples/icebreaker-workshop/stopwatch + # TODO(leonardt): Merge with top-level coverage + pytest --pycodestyle stopwatch.py test_stopwatch.py + cd - + - name: Test riscv_mini run: | cd examples/riscv_mini diff --git a/examples/icebreaker-workshop/README.md b/examples/icebreaker-workshop/README.md new file mode 100644 index 000000000..5f52ff9f7 --- /dev/null +++ b/examples/icebreaker-workshop/README.md @@ -0,0 +1,6 @@ +# iCEBreaker FPGA Workshop Magma Edition +Original workshop: https://github.com/icebreaker-fpga/icebreaker-workshop + +Install oss-cad-suite: https://github.com/YosysHQ/oss-cad-suite-build?tab=readme-ov-file#installation + +Install system verilog yosys plugin: https://github.com/chipsalliance/synlig diff --git a/examples/icebreaker-workshop/stopwatch/.gitignore b/examples/icebreaker-workshop/stopwatch/.gitignore new file mode 100644 index 000000000..9647f787f --- /dev/null +++ b/examples/icebreaker-workshop/stopwatch/.gitignore @@ -0,0 +1,9 @@ +stopwatch.bin +stopwatch.json +stopwatch.asc +stopwatch.yslog +stopwatch.nplog +stopwatch.mlir +stopwatch.sv +stopwatch.rpt +slpp_all diff --git a/examples/icebreaker-workshop/stopwatch/Makefile b/examples/icebreaker-workshop/stopwatch/Makefile new file mode 100644 index 000000000..4a18eb2a1 --- /dev/null +++ b/examples/icebreaker-workshop/stopwatch/Makefile @@ -0,0 +1,49 @@ +PROJ = stopwatch + +all: $(PROJ).rpt $(PROJ).bin + +$(PROJ).sv: $(PROJ).py + python3 $< + +$(PROJ).json: $(PROJ).sv + yosys -ql $(PROJ).yslog -p "plugin -i systemverilog" -p "read_systemverilog $<" -p 'synth_ice40 -top top -json $@' + +$(PROJ).asc: $(PROJ).json icebreaker.pcf + nextpnr-ice40 -ql $(PROJ).nplog --up5k --package sg48 --freq 12 --asc $@ --pcf icebreaker.pcf --json $< + +$(PROJ).bin: $(PROJ).asc + icepack $< $@ + +$(PROJ).rpt: $(PROJ).asc + icetime -d up5k -c 12 -mtr $@ $< + +$(PROJ)_tb: $(PROJ)_tb.v $(PROJ).v + iverilog -o $@ $^ + +$(PROJ)_tb.vcd: $(PROJ)_tb + vvp -N $< +vcd=$@ + +$(PROJ)_syn.v: $(PROJ).json + yosys -p 'read_json $^; write_verilog $@' + +$(PROJ)_syntb: $(PROJ)_tb.v $(PROJ)_syn.v + iverilog -o $@ $^ `yosys-config --datdir/ice40/cells_sim.v` + +$(PROJ)_syntb.vcd: $(PROJ)_syntb + vvp -N $< +vcd=$@ + +prog: $(PROJ).bin + iceprog $< + +sudo-prog: $(PROJ).bin + @echo 'Executing prog as root!!!' + sudo iceprog $< + +clean: + rm -f $(PROJ).yslog $(PROJ).nplog $(PROJ).json $(PROJ).asc $(PROJ).rpt $(PROJ).bin + rm -f $(PROJ)_tb $(PROJ)_tb.vcd $(PROJ)_syn.v $(PROJ)_syntb $(PROJ)_syntb.vcd + rm -f $(PROJ).sv $(PROJ).mlir + rm -rf slpp_all/ + +.SECONDARY: +.PHONY: all prog clean diff --git a/examples/icebreaker-workshop/stopwatch/_stopwatch.v b/examples/icebreaker-workshop/stopwatch/_stopwatch.v new file mode 100644 index 000000000..036ad4ff9 --- /dev/null +++ b/examples/icebreaker-workshop/stopwatch/_stopwatch.v @@ -0,0 +1,179 @@ +// Cause yosys to throw an error when we implicitly declare nets +`default_nettype none + +// Project entry point +module top ( + input CLK, + input BTN_N, BTN1, BTN2, BTN3, + output LED1, LED2, LED3, LED4, LED5, + output P1A1, P1A2, P1A3, P1A4, P1A7, P1A8, P1A9, P1A10, +); + // 7 segment control line bus + wire [7:0] seven_segment; + + // Assign 7 segment control line bus to Pmod pins + assign { P1A10, P1A9, P1A8, P1A7, P1A4, P1A3, P1A2, P1A1 } = seven_segment; + + // Display value register and increment bus + reg [7:0] display_value = 0; + wire [7:0] display_value_inc; + + // Lap registers + reg [7:0] lap_value = 0; + reg [4:0] lap_timeout = 0; + + // Clock divider and pulse registers + reg [20:0] clkdiv = 0; + reg clkdiv_pulse = 0; + reg running = 0; + + // Combinatorial logic + assign LED1 = BTN1 && BTN2; + assign LED2 = BTN1 && BTN3; + assign LED3 = BTN2 && BTN3; + assign LED4 = !BTN_N; + assign LED5 = !BTN_N || BTN1 || BTN2 || BTN3; + + // Synchronous logic + always @(posedge CLK) begin + // Clock divider pulse generator + if (clkdiv == 1200000) begin + clkdiv <= 0; + clkdiv_pulse <= 1; + end else begin + clkdiv <= clkdiv + 1; + clkdiv_pulse <= 0; + end + + // Lap timeout counter + if (clkdiv_pulse && lap_timeout) begin + lap_timeout <= lap_timeout - 1; + end + + // Timer counter + if (clkdiv_pulse && running) begin + display_value <= display_value_inc; + end + + // Button controls + if (!BTN_N) begin + display_value <= 0; + running <= 0; + lap_timeout <= 0; + end + + if (BTN3) begin + running <= 1; + end + + if (BTN1) begin + running <= 0; + end + + if (BTN2) begin + lap_value <= display_value; + lap_timeout <= 20; + end + end + + // BCD counter + bcd8_increment bot_inc ( + .din(display_value), + .dout(display_value_inc) + ); + + // 7 segment display control + seven_seg_ctrl seven_segment_ctrl ( + .CLK(CLK), + .din(lap_timeout ? lap_value[7:0] : display_value[7:0]), + .dout(seven_segment) + ); + +endmodule + +// BCD (Binary Coded Decimal) counter +module bcd8_increment ( + input [7:0] din, + output reg [7:0] dout +); + always @* begin + case (1'b1) + din[7:0] == 8'h 99: + dout = 0; + din[3:0] == 4'h 9: + dout = {din[7:4] + 4'd 1, 4'h 0}; + default: + dout = {din[7:4], din[3:0] + 4'd 1}; + endcase + end +endmodule + +// Seven segment controller +// Switches quickly between the two parts of the display +// to create the illusion of both halfs being illuminated +// at the same time. +module seven_seg_ctrl ( + input CLK, + input [7:0] din, + output reg [7:0] dout +); + wire [6:0] lsb_digit; + wire [6:0] msb_digit; + + seven_seg_hex msb_nibble ( + .din(din[7:4]), + .dout(msb_digit) + ); + + seven_seg_hex lsb_nibble ( + .din(din[3:0]), + .dout(lsb_digit) + ); + + reg [9:0] clkdiv = 0; + reg clkdiv_pulse = 0; + reg msb_not_lsb = 0; + + always @(posedge CLK) begin + clkdiv <= clkdiv + 1; + clkdiv_pulse <= &clkdiv; + msb_not_lsb <= msb_not_lsb ^ clkdiv_pulse; + + if (clkdiv_pulse) begin + if (msb_not_lsb) begin + dout[6:0] <= ~msb_digit; + dout[7] <= 0; + end else begin + dout[6:0] <= ~lsb_digit; + dout[7] <= 1; + end + end + end +endmodule + +// Convert 4bit numbers to 7 segments +module seven_seg_hex ( + input [3:0] din, + output reg [6:0] dout +); + always @* + case (din) + 4'h0: dout = 7'b 0111111; + 4'h1: dout = 7'b 0000110; + 4'h2: dout = 7'b 1011011; + 4'h3: dout = 7'b 1001111; + 4'h4: dout = 7'b 1100110; + 4'h5: dout = 7'b 1101101; + 4'h6: dout = 7'b 1111101; + 4'h7: dout = 7'b 0000111; + 4'h8: dout = 7'b 1111111; + 4'h9: dout = 7'b 1101111; + 4'hA: dout = 7'b 1110111; + 4'hB: dout = 7'b 1111100; + 4'hC: dout = 7'b 0111001; + 4'hD: dout = 7'b 1011110; + 4'hE: dout = 7'b 1111001; + 4'hF: dout = 7'b 1110001; + default: dout = 7'b 1000000; + endcase +endmodule diff --git a/examples/icebreaker-workshop/stopwatch/build/.gitignore b/examples/icebreaker-workshop/stopwatch/build/.gitignore new file mode 100644 index 000000000..d6b7ef32c --- /dev/null +++ b/examples/icebreaker-workshop/stopwatch/build/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/examples/icebreaker-workshop/stopwatch/gold/stopwatch.mlir b/examples/icebreaker-workshop/stopwatch/gold/stopwatch.mlir new file mode 100644 index 000000000..862be3d47 --- /dev/null +++ b/examples/icebreaker-workshop/stopwatch/gold/stopwatch.mlir @@ -0,0 +1,368 @@ +module attributes {circt.loweringOptions = "locationInfoStyle=none,omitVersionComment"} { + hw.module @Counter(in %CLK: i1, out O: i21, out COUT: i1) { + %0 = hw.constant 1 : i21 + %2 = comb.add %1, %0 : i21 + %3 = hw.constant 0 : i21 + %4 = hw.constant 1199999 : i21 + %5 = comb.icmp eq %1, %4 : i21 + %7 = hw.array_create %3, %2 : i21 + %6 = hw.array_get %7[%5] : !hw.array<2xi21>, i1 + %8 = sv.reg name "Register_inst0" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %8, %6 : i21 + } + %9 = hw.constant 0 : i21 + sv.initial { + sv.bpassign %8, %9 : i21 + } + %1 = sv.read_inout %8 : !hw.inout + %10 = hw.constant 1199999 : i21 + %11 = comb.icmp eq %1, %10 : i21 + hw.output %1, %11 : i21, i1 + } + hw.module @bcd8_increment(in %din: i8, out dout: i8) { + %0 = hw.constant 99 : i8 + %1 = comb.icmp eq %din, %0 : i8 + %2 = hw.constant 0 : i8 + %3 = comb.extract %din from 0 : (i8) -> i4 + %4 = hw.constant 9 : i4 + %5 = comb.icmp eq %3, %4 : i4 + %6 = hw.constant 0 : i1 + %7 = comb.extract %din from 4 : (i8) -> i1 + %8 = comb.extract %din from 5 : (i8) -> i1 + %9 = comb.extract %din from 6 : (i8) -> i1 + %10 = comb.extract %din from 7 : (i8) -> i1 + %11 = comb.concat %10, %9, %8, %7 : i1, i1, i1, i1 + %12 = hw.constant 1 : i4 + %13 = comb.add %11, %12 : i4 + %14 = comb.extract %13 from 0 : (i4) -> i1 + %15 = comb.extract %13 from 1 : (i4) -> i1 + %16 = comb.extract %13 from 2 : (i4) -> i1 + %17 = comb.extract %13 from 3 : (i4) -> i1 + %18 = comb.extract %din from 0 : (i8) -> i1 + %19 = comb.extract %din from 1 : (i8) -> i1 + %20 = comb.extract %din from 2 : (i8) -> i1 + %21 = comb.extract %din from 3 : (i8) -> i1 + %22 = comb.concat %21, %20, %19, %18 : i1, i1, i1, i1 + %23 = hw.constant 1 : i4 + %24 = comb.add %22, %23 : i4 + %25 = comb.extract %24 from 0 : (i4) -> i1 + %26 = comb.extract %24 from 1 : (i4) -> i1 + %27 = comb.extract %24 from 2 : (i4) -> i1 + %28 = comb.extract %24 from 3 : (i4) -> i1 + %30 = sv.reg : !hw.inout + %29 = sv.read_inout %30 : !hw.inout + sv.alwayscomb { + sv.if %1 { + %31 = comb.concat %6, %6, %6, %6, %6, %6, %6, %6 : i1, i1, i1, i1, i1, i1, i1, i1 + sv.bpassign %30, %31 : i8 + } else { + sv.if %5 { + %32 = comb.concat %17, %16, %15, %14, %6, %6, %6, %6 : i1, i1, i1, i1, i1, i1, i1, i1 + sv.bpassign %30, %32 : i8 + } else { + %33 = comb.concat %10, %9, %8, %7, %28, %27, %26, %25 : i1, i1, i1, i1, i1, i1, i1, i1 + sv.bpassign %30, %33 : i8 + } + } + } + hw.output %29 : i8 + } + hw.module @seven_seg_hex(in %din: i4, out dout: i7) { + %0 = hw.constant 64 : i7 + %1 = hw.constant 63 : i7 + %2 = hw.constant 0 : i4 + %3 = comb.icmp eq %din, %2 : i4 + %5 = hw.array_create %1, %0 : i7 + %4 = hw.array_get %5[%3] : !hw.array<2xi7>, i1 + %6 = hw.constant 6 : i7 + %7 = hw.constant 1 : i4 + %8 = comb.icmp eq %din, %7 : i4 + %10 = hw.array_create %6, %4 : i7 + %9 = hw.array_get %10[%8] : !hw.array<2xi7>, i1 + %11 = hw.constant 91 : i7 + %12 = hw.constant 2 : i4 + %13 = comb.icmp eq %din, %12 : i4 + %15 = hw.array_create %11, %9 : i7 + %14 = hw.array_get %15[%13] : !hw.array<2xi7>, i1 + %16 = hw.constant 79 : i7 + %17 = hw.constant 3 : i4 + %18 = comb.icmp eq %din, %17 : i4 + %20 = hw.array_create %16, %14 : i7 + %19 = hw.array_get %20[%18] : !hw.array<2xi7>, i1 + %21 = hw.constant 102 : i7 + %22 = hw.constant 4 : i4 + %23 = comb.icmp eq %din, %22 : i4 + %25 = hw.array_create %21, %19 : i7 + %24 = hw.array_get %25[%23] : !hw.array<2xi7>, i1 + %26 = hw.constant 109 : i7 + %27 = hw.constant 5 : i4 + %28 = comb.icmp eq %din, %27 : i4 + %30 = hw.array_create %26, %24 : i7 + %29 = hw.array_get %30[%28] : !hw.array<2xi7>, i1 + %31 = hw.constant 125 : i7 + %32 = hw.constant 6 : i4 + %33 = comb.icmp eq %din, %32 : i4 + %35 = hw.array_create %31, %29 : i7 + %34 = hw.array_get %35[%33] : !hw.array<2xi7>, i1 + %36 = hw.constant 7 : i7 + %37 = hw.constant 7 : i4 + %38 = comb.icmp eq %din, %37 : i4 + %40 = hw.array_create %36, %34 : i7 + %39 = hw.array_get %40[%38] : !hw.array<2xi7>, i1 + %41 = hw.constant 127 : i7 + %42 = hw.constant 8 : i4 + %43 = comb.icmp eq %din, %42 : i4 + %45 = hw.array_create %41, %39 : i7 + %44 = hw.array_get %45[%43] : !hw.array<2xi7>, i1 + %46 = hw.constant 111 : i7 + %47 = hw.constant 9 : i4 + %48 = comb.icmp eq %din, %47 : i4 + %50 = hw.array_create %46, %44 : i7 + %49 = hw.array_get %50[%48] : !hw.array<2xi7>, i1 + %51 = hw.constant 119 : i7 + %52 = hw.constant 10 : i4 + %53 = comb.icmp eq %din, %52 : i4 + %55 = hw.array_create %51, %49 : i7 + %54 = hw.array_get %55[%53] : !hw.array<2xi7>, i1 + %56 = hw.constant 124 : i7 + %57 = hw.constant 11 : i4 + %58 = comb.icmp eq %din, %57 : i4 + %60 = hw.array_create %56, %54 : i7 + %59 = hw.array_get %60[%58] : !hw.array<2xi7>, i1 + %61 = hw.constant 57 : i7 + %62 = hw.constant 12 : i4 + %63 = comb.icmp eq %din, %62 : i4 + %65 = hw.array_create %61, %59 : i7 + %64 = hw.array_get %65[%63] : !hw.array<2xi7>, i1 + %66 = hw.constant 94 : i7 + %67 = hw.constant 13 : i4 + %68 = comb.icmp eq %din, %67 : i4 + %70 = hw.array_create %66, %64 : i7 + %69 = hw.array_get %70[%68] : !hw.array<2xi7>, i1 + %71 = hw.constant 121 : i7 + %72 = hw.constant 14 : i4 + %73 = comb.icmp eq %din, %72 : i4 + %75 = hw.array_create %71, %69 : i7 + %74 = hw.array_get %75[%73] : !hw.array<2xi7>, i1 + %76 = hw.constant 113 : i7 + %77 = hw.constant 15 : i4 + %78 = comb.icmp eq %din, %77 : i4 + %80 = hw.array_create %76, %74 : i7 + %79 = hw.array_get %80[%78] : !hw.array<2xi7>, i1 + hw.output %79 : i7 + } + hw.module @Counter_unq1(in %CLK: i1, out O: i10) { + %0 = hw.constant 1 : i10 + %2 = comb.add %1, %0 : i10 + %3 = sv.reg name "Register_inst0" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %3, %2 : i10 + } + %4 = hw.constant 0 : i10 + sv.initial { + sv.bpassign %3, %4 : i10 + } + %1 = sv.read_inout %3 : !hw.inout + hw.output %1 : i10 + } + hw.module @seven_seg_ctrl(in %CLK: i1, in %din: i8, out dout: i8) { + %0 = hw.instance "Counter_inst0" @Counter_unq1(CLK: %CLK: i1) -> (O: i10) + %2 = hw.constant -1 : i10 + %1 = comb.icmp eq %0, %2 : i10 + %4 = sv.reg name "Register_inst1" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %4, %1 : i1 + } + %5 = hw.constant 0 : i1 + sv.initial { + sv.bpassign %4, %5 : i1 + } + %3 = sv.read_inout %4 : !hw.inout + %7 = comb.xor %6, %3 : i1 + %8 = sv.reg name "Register_inst2" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %8, %7 : i1 + } + sv.initial { + sv.bpassign %8, %5 : i1 + } + %6 = sv.read_inout %8 : !hw.inout + %9 = comb.extract %din from 4 : (i8) -> i4 + %10 = hw.instance "seven_seg_hex_inst0" @seven_seg_hex(din: %9: i4) -> (dout: i7) + %12 = hw.constant -1 : i7 + %11 = comb.xor %12, %10 : i7 + %13 = hw.constant 0 : i1 + %14 = comb.extract %din from 0 : (i8) -> i4 + %15 = hw.instance "seven_seg_hex_inst1" @seven_seg_hex(din: %14: i4) -> (dout: i7) + %16 = comb.xor %12, %15 : i7 + %17 = hw.constant 1 : i1 + %19 = comb.extract %18 from 0 : (i8) -> i7 + %20 = comb.extract %18 from 7 : (i8) -> i1 + %23 = sv.reg : !hw.inout + %21 = sv.read_inout %23 : !hw.inout + %24 = sv.reg : !hw.inout + %22 = sv.read_inout %24 : !hw.inout + sv.alwayscomb { + sv.bpassign %23, %19 : i7 + sv.bpassign %24, %20 : i1 + sv.if %3 { + sv.if %6 { + sv.bpassign %23, %11 : i7 + sv.bpassign %24, %13 : i1 + } else { + sv.bpassign %23, %16 : i7 + sv.bpassign %24, %17 : i1 + } + } + } + %25 = comb.concat %22, %21 : i1, i7 + %26 = sv.reg name "Register_inst0" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %26, %25 : i8 + } + %27 = hw.constant 0 : i8 + sv.initial { + sv.bpassign %26, %27 : i8 + } + %18 = sv.read_inout %26 : !hw.inout + hw.output %18 : i8 + } + hw.module @top(in %CLK: i1, in %BTN_N: i1, in %BTN1: i1, in %BTN2: i1, in %BTN3: i1, out LED1: i1, out LED2: i1, out LED3: i1, out LED4: i1, out LED5: i1, out P1A1: i1, out P1A2: i1, out P1A3: i1, out P1A4: i1, out P1A7: i1, out P1A8: i1, out P1A9: i1, out P1A10: i1) { + %0 = comb.and %BTN1, %BTN2 : i1 + %1 = comb.and %BTN1, %BTN3 : i1 + %2 = comb.and %BTN2, %BTN3 : i1 + %4 = hw.constant -1 : i1 + %3 = comb.xor %4, %BTN_N : i1 + %5 = comb.xor %4, %BTN_N : i1 + %6 = comb.or %5, %BTN1 : i1 + %7 = comb.or %6, %BTN2 : i1 + %8 = comb.or %7, %BTN3 : i1 + %9 = comb.xor %4, %BTN_N : i1 + %10, %11 = hw.instance "Counter_inst0" @Counter(CLK: %CLK: i1) -> (O: i21, COUT: i1) + %12 = hw.constant 1 : i1 + %15 = sv.reg : !hw.inout + %14 = sv.read_inout %15 : !hw.inout + sv.alwayscomb { + sv.bpassign %15, %13 : i1 + sv.if %BTN3 { + sv.bpassign %15, %12 : i1 + } + } + %16 = hw.constant 0 : i1 + %18 = sv.reg : !hw.inout + %17 = sv.read_inout %18 : !hw.inout + sv.alwayscomb { + sv.bpassign %18, %14 : i1 + sv.if %BTN1 { + sv.bpassign %18, %16 : i1 + } + } + %20 = sv.reg name "Register_inst3" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %20, %17 : i1 + } + %21 = hw.constant 0 : i1 + sv.initial { + sv.bpassign %20, %21 : i1 + } + %19 = sv.read_inout %20 : !hw.inout + %22 = comb.and %11, %19 : i1 + %24 = hw.instance "bcd8_increment_inst0" @bcd8_increment(din: %23: i8) -> (dout: i8) + %26 = sv.reg : !hw.inout + %25 = sv.read_inout %26 : !hw.inout + sv.alwayscomb { + sv.bpassign %26, %23 : i8 + sv.if %22 { + sv.bpassign %26, %24 : i8 + } + } + %27 = hw.constant 0 : i8 + %28 = hw.constant 0 : i1 + %29 = hw.constant 20 : i5 + %32 = sv.reg name "Register_inst1" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %32, %30 : i8 + } + %33 = hw.constant 0 : i8 + sv.initial { + sv.bpassign %32, %33 : i8 + } + %31 = sv.read_inout %32 : !hw.inout + %36 = sv.reg : !hw.inout + %30 = sv.read_inout %36 : !hw.inout + %37 = sv.reg : !hw.inout + %35 = sv.read_inout %37 : !hw.inout + sv.alwayscomb { + sv.bpassign %37, %34 : i5 + sv.bpassign %36, %31 : i8 + sv.if %BTN2 { + sv.bpassign %36, %23 : i8 + sv.bpassign %37, %29 : i5 + } + } + %39 = sv.reg name "Register_inst2" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %39, %35 : i5 + } + %40 = hw.constant 0 : i5 + sv.initial { + sv.bpassign %39, %40 : i5 + } + %38 = sv.read_inout %39 : !hw.inout + %41 = hw.constant 0 : i5 + %42 = comb.icmp eq %38, %41 : i5 + %43 = comb.xor %4, %42 : i1 + %44 = comb.and %11, %43 : i1 + %45 = hw.constant 1 : i5 + %46 = comb.sub %38, %45 : i5 + %48 = sv.reg : !hw.inout + %47 = sv.read_inout %48 : !hw.inout + sv.alwayscomb { + sv.bpassign %48, %38 : i5 + sv.if %44 { + sv.bpassign %48, %46 : i5 + } + } + %49 = hw.constant 0 : i5 + %51 = sv.reg : !hw.inout + %50 = sv.read_inout %51 : !hw.inout + %52 = sv.reg : !hw.inout + %13 = sv.read_inout %52 : !hw.inout + %53 = sv.reg : !hw.inout + %34 = sv.read_inout %53 : !hw.inout + sv.alwayscomb { + sv.bpassign %51, %25 : i8 + sv.bpassign %53, %47 : i5 + sv.bpassign %52, %19 : i1 + sv.if %9 { + sv.bpassign %51, %27 : i8 + sv.bpassign %52, %28 : i1 + sv.bpassign %53, %49 : i5 + } + } + %54 = sv.reg name "Register_inst0" : !hw.inout + sv.alwaysff(posedge %CLK) { + sv.passign %54, %50 : i8 + } + sv.initial { + sv.bpassign %54, %33 : i8 + } + %23 = sv.read_inout %54 : !hw.inout + %55 = hw.constant 0 : i5 + %56 = comb.icmp eq %38, %55 : i5 + %57 = comb.xor %4, %56 : i1 + %59 = hw.array_create %31, %23 : i8 + %58 = hw.array_get %59[%57] : !hw.array<2xi8>, i1 + %60 = hw.instance "seven_seg_ctrl_inst0" @seven_seg_ctrl(CLK: %CLK: i1, din: %58: i8) -> (dout: i8) + %61 = comb.extract %60 from 0 : (i8) -> i1 + %62 = comb.extract %60 from 1 : (i8) -> i1 + %63 = comb.extract %60 from 2 : (i8) -> i1 + %64 = comb.extract %60 from 3 : (i8) -> i1 + %65 = comb.extract %60 from 4 : (i8) -> i1 + %66 = comb.extract %60 from 5 : (i8) -> i1 + %67 = comb.extract %60 from 6 : (i8) -> i1 + %68 = comb.extract %60 from 7 : (i8) -> i1 + hw.output %0, %1, %2, %3, %8, %61, %62, %63, %64, %65, %66, %67, %68 : i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1, i1 + } +} diff --git a/examples/icebreaker-workshop/stopwatch/icebreaker.pcf b/examples/icebreaker-workshop/stopwatch/icebreaker.pcf new file mode 100644 index 000000000..1164c98f3 --- /dev/null +++ b/examples/icebreaker-workshop/stopwatch/icebreaker.pcf @@ -0,0 +1,54 @@ +# 12 MHz clock +set_io -nowarn CLK 35 + +# RS232 +set_io -nowarn RX 6 +set_io -nowarn TX 9 + +# LEDs and Button +set_io -nowarn BTN_N 10 +set_io -nowarn LEDR_N 11 +set_io -nowarn LEDG_N 37 + +# RGB LED Driver +set_io -nowarn LED_RED_N 39 +set_io -nowarn LED_GRN_N 40 +set_io -nowarn LED_BLU_N 41 + +# SPI Flash +set_io -nowarn FLASH_SCK 15 +set_io -nowarn FLASH_SSB 16 +set_io -nowarn FLASH_IO0 14 +set_io -nowarn FLASH_IO1 17 +set_io -nowarn FLASH_IO2 12 +set_io -nowarn FLASH_IO3 13 + +# PMOD 1A +set_io -nowarn P1A1 4 +set_io -nowarn P1A2 2 +set_io -nowarn P1A3 47 +set_io -nowarn P1A4 45 +set_io -nowarn P1A7 3 +set_io -nowarn P1A8 48 +set_io -nowarn P1A9 46 +set_io -nowarn P1A10 44 + +# PMOD 1B +set_io -nowarn P1B1 43 +set_io -nowarn P1B2 38 +set_io -nowarn P1B3 34 +set_io -nowarn P1B4 31 +set_io -nowarn P1B7 42 +set_io -nowarn P1B8 36 +set_io -nowarn P1B9 32 +set_io -nowarn P1B10 28 + +# LEDs and Buttons (PMOD 2) +set_io -nowarn LED1 26 +set_io -nowarn LED2 27 +set_io -nowarn LED3 25 +set_io -nowarn LED4 23 +set_io -nowarn LED5 21 +set_io -nowarn BTN1 20 +set_io -nowarn BTN2 19 +set_io -nowarn BTN3 18 diff --git a/examples/icebreaker-workshop/stopwatch/stopwatch.py b/examples/icebreaker-workshop/stopwatch/stopwatch.py new file mode 100644 index 000000000..6e79e2707 --- /dev/null +++ b/examples/icebreaker-workshop/stopwatch/stopwatch.py @@ -0,0 +1,143 @@ +import magma as m + + +class bcd8_increment(m.Circuit): + io = m.IO(din=m.In(m.UInt[8]), dout=m.Out(m.UInt[8])) + with m.when(io.din == 99): + io.dout @= 0 + with m.elsewhen(io.din[:4] == 9): + io.dout @= m.concat(m.UInt[4](0), io.din[4:] + 1) + with m.otherwise(): + io.dout @= m.concat(io.din[:4] + 1, io.din[4:]) + + +class seven_seg_hex(m.Circuit): + io = m.IO(din=m.In(m.Bits[4]), dout=m.Out(m.Bits[7])) + # TODO: Could add T arg to dict lookup or magma dict constructor for T to + # avoid T literal here + io.dout @= m.list_lookup([ + m.Bits[7](0b0111111), + m.Bits[7](0b0000110), + m.Bits[7](0b1011011), + m.Bits[7](0b1001111), + m.Bits[7](0b1100110), + m.Bits[7](0b1101101), + m.Bits[7](0b1111101), + m.Bits[7](0b0000111), + m.Bits[7](0b1111111), + m.Bits[7](0b1101111), + m.Bits[7](0b1110111), + m.Bits[7](0b1111100), + m.Bits[7](0b0111001), + m.Bits[7](0b1011110), + m.Bits[7](0b1111001), + m.Bits[7](0b1110001) + ], io.din, default=0b1000000) + + +class seven_seg_ctrl(m.Circuit): + io = m.IO(CLK=m.In(m.Clock), + din=m.In(m.Bits[8]), + dout=m.Out(m.Bits[8])) + dout_reg = m.Register(m.Bits[8])() + io.dout @= dout_reg + + msb_digit = seven_seg_hex()(io.din[4:]) + lsb_digit = seven_seg_hex()(io.din[:4]) + + clkdiv = m.mantle.Counter(2 ** 10)() + clkdiv_pulse = m.Register(m.Bit)() + msb_not_lsb = m.Register(m.Bit)() + + clkdiv_pulse.I @= clkdiv.O.reduce_and() + msb_not_lsb.I @= msb_not_lsb.O ^ clkdiv_pulse.O + + # TODO: Double check synchronous logic here + with m.when(clkdiv_pulse.O): + with m.when(msb_not_lsb.O): + dout_reg.I[:7] @= ~msb_digit + dout_reg.I[7] @= 0 + with m.otherwise(): + dout_reg.I[:7] @= ~lsb_digit + dout_reg.I[7] @= 1 + + +class top(m.Circuit): + io = m.IO( + CLK=m.In(m.Clock), + BTN_N=m.In(m.Bit), + BTN1=m.In(m.Bit), + BTN2=m.In(m.Bit), + BTN3=m.In(m.Bit), + + LED1=m.Out(m.Bit), + LED2=m.Out(m.Bit), + LED3=m.Out(m.Bit), + LED4=m.Out(m.Bit), + LED5=m.Out(m.Bit), + + P1A1=m.Out(m.Bit), + P1A2=m.Out(m.Bit), + P1A3=m.Out(m.Bit), + P1A4=m.Out(m.Bit), + P1A7=m.Out(m.Bit), + P1A8=m.Out(m.Bit), + P1A9=m.Out(m.Bit), + P1A10=m.Out(m.Bit) + ) + + seven_segment = m.array( + list(reversed([io.P1A10, io.P1A9, io.P1A8, io.P1A7, io.P1A4, io.P1A3, + io.P1A2, io.P1A1])) + ) + + display_value = m.Register(m.Bits[8])() + display_value_inc = m.Bits[8]() + + lap_value = m.Register(m.Bits[8])() + lap_timeout = m.Register(m.Bits[5])() + + clkdiv = m.mantle.Counter(1200000, has_cout=True)() + clkdiv_pulse = clkdiv.COUT + + with m.when(clkdiv_pulse & (lap_timeout.O != 0)): + lap_timeout.I @= lap_timeout.O - 1 + + running = m.Register(m.Bit)() + + with m.when(clkdiv_pulse & running.O): + display_value.I @= display_value_inc + + with m.when(~io.BTN_N): + display_value.I @= 0 + running.I @= 0 + lap_timeout.I @= 0 + + with m.when(io.BTN3): + running.I @= 1 + + with m.when(io.BTN1): + running.I @= 0 + + with m.when(io.BTN2): + lap_value.I @= display_value.O + lap_timeout.I @= 20 + + io.LED1 @= io.BTN1 & io.BTN2 + io.LED2 @= io.BTN1 & io.BTN3 + io.LED3 @= io.BTN2 & io.BTN3 + io.LED4 @= ~io.BTN_N + io.LED5 @= ~io.BTN_N | io.BTN1 | io.BTN2 | io.BTN3 + + display_value_inc @= bcd8_increment()(display_value.O) + # TODO: .ite() on Bits type doesn't work with mlir, have to do truth check explicitly + # seven_segment @= seven_seg_ctrl()(lap_timeout.O.ite(lap_value.O[:8], display_value.O[:8]), CLK=io.CLK) + seven_segment @= seven_seg_ctrl()( + (lap_timeout.O != 0).ite(lap_value.O[:8], display_value.O[:8]), + CLK=io.CLK + ) + + +if __name__ == "__main__": + m.compile("stopwatch", top, output="mlir-verilog", sv=True, + disallow_local_variables=True) diff --git a/examples/icebreaker-workshop/stopwatch/stopwatch.v b/examples/icebreaker-workshop/stopwatch/stopwatch.v new file mode 100644 index 000000000..6bb8a676d --- /dev/null +++ b/examples/icebreaker-workshop/stopwatch/stopwatch.v @@ -0,0 +1,246 @@ +module Counter( + input CLK, + output [20:0] O, + output COUT +); + + reg [20:0] Register_inst0; + always_ff @(posedge CLK) begin + automatic logic [1:0][20:0] _GEN; + _GEN = {{21'h0}, {Register_inst0 + 21'h1}}; + Register_inst0 <= _GEN[Register_inst0 == 21'h124F7F]; + end // always_ff @(posedge) + initial + Register_inst0 = 21'h0; + assign O = Register_inst0; + assign COUT = Register_inst0 == 21'h124F7F; +endmodule + +module bcd8_increment( + input [7:0] din, + output [7:0] dout +); + + reg [7:0] _GEN; + always_comb begin + if (din == 8'h63) + _GEN = {1'h0, 1'h0, 1'h0, 1'h0, 1'h0, 1'h0, 1'h0, 1'h0}; + else if (din[3:0] == 4'h9) begin + automatic logic [3:0] _GEN_0 = din[7:4] + 4'h1; + _GEN = {1'h0, 1'h0, 1'h0, 1'h0, _GEN_0[3], _GEN_0[2], _GEN_0[1], _GEN_0[0]}; + end + else begin + automatic logic [3:0] _GEN_1 = din[3:0] + 4'h1; + _GEN = {_GEN_1[3], _GEN_1[2], _GEN_1[1], _GEN_1[0], din[7], din[6], din[5], din[4]}; + end + end // always_comb + assign dout = _GEN; +endmodule + +module seven_seg_hex( + input [3:0] din, + output [6:0] dout +); + + wire [1:0][6:0] _GEN = '{7'h3F, 7'h40}; + wire [1:0][6:0] _GEN_0 = {{7'h6}, {_GEN[din == 4'h0]}}; + wire [1:0][6:0] _GEN_1 = {{7'h5B}, {_GEN_0[din == 4'h1]}}; + wire [1:0][6:0] _GEN_2 = {{7'h4F}, {_GEN_1[din == 4'h2]}}; + wire [1:0][6:0] _GEN_3 = {{7'h66}, {_GEN_2[din == 4'h3]}}; + wire [1:0][6:0] _GEN_4 = {{7'h6D}, {_GEN_3[din == 4'h4]}}; + wire [1:0][6:0] _GEN_5 = {{7'h7D}, {_GEN_4[din == 4'h5]}}; + wire [1:0][6:0] _GEN_6 = {{7'h7}, {_GEN_5[din == 4'h6]}}; + wire [1:0][6:0] _GEN_7 = {{7'h7F}, {_GEN_6[din == 4'h7]}}; + wire [1:0][6:0] _GEN_8 = {{7'h6F}, {_GEN_7[din == 4'h8]}}; + wire [1:0][6:0] _GEN_9 = {{7'h77}, {_GEN_8[din == 4'h9]}}; + wire [1:0][6:0] _GEN_10 = {{7'h7C}, {_GEN_9[din == 4'hA]}}; + wire [1:0][6:0] _GEN_11 = {{7'h39}, {_GEN_10[din == 4'hB]}}; + wire [1:0][6:0] _GEN_12 = {{7'h5E}, {_GEN_11[din == 4'hC]}}; + wire [1:0][6:0] _GEN_13 = {{7'h79}, {_GEN_12[din == 4'hD]}}; + wire [1:0][6:0] _GEN_14 = {{7'h71}, {_GEN_13[din == 4'hE]}}; + assign dout = _GEN_14[&din]; +endmodule + +module Counter_unq1( + input CLK, + output [9:0] O +); + + reg [9:0] Register_inst0; + always_ff @(posedge CLK) + Register_inst0 <= Register_inst0 + 10'h1; + initial + Register_inst0 = 10'h0; + assign O = Register_inst0; +endmodule + +module seven_seg_ctrl( + input CLK, + input [7:0] din, + output [7:0] dout +); + + reg [7:0] Register_inst0; + wire [6:0] _seven_seg_hex_inst1_dout; + wire [6:0] _seven_seg_hex_inst0_dout; + wire [9:0] _Counter_inst0_O; + reg Register_inst1; + reg Register_inst2; + reg [6:0] _GEN; + reg _GEN_0; + always_comb begin + _GEN = Register_inst0[6:0]; + _GEN_0 = Register_inst0[7]; + if (Register_inst1) begin + if (Register_inst2) begin + _GEN = ~_seven_seg_hex_inst0_dout; + _GEN_0 = 1'h0; + end + else begin + _GEN = ~_seven_seg_hex_inst1_dout; + _GEN_0 = 1'h1; + end + end + end // always_comb + always_ff @(posedge CLK) begin + Register_inst1 <= &_Counter_inst0_O; + Register_inst2 <= Register_inst2 ^ Register_inst1; + Register_inst0 <= {_GEN_0, _GEN}; + end // always_ff @(posedge) + initial begin + Register_inst1 = 1'h0; + Register_inst2 = 1'h0; + Register_inst0 = 8'h0; + end // initial + Counter_unq1 Counter_inst0 ( + .CLK (CLK), + .O (_Counter_inst0_O) + ); + seven_seg_hex seven_seg_hex_inst0 ( + .din (din[7:4]), + .dout (_seven_seg_hex_inst0_dout) + ); + seven_seg_hex seven_seg_hex_inst1 ( + .din (din[3:0]), + .dout (_seven_seg_hex_inst1_dout) + ); + assign dout = Register_inst0; +endmodule + +module top( + input CLK, + BTN_N, + BTN1, + BTN2, + BTN3, + output LED1, + LED2, + LED3, + LED4, + LED5, + P1A1, + P1A2, + P1A3, + P1A4, + P1A7, + P1A8, + P1A9, + P1A10 +); + + reg [7:0] Register_inst0; + reg [4:0] _GEN; + reg _GEN_0; + wire [7:0] _seven_seg_ctrl_inst0_dout; + wire [7:0] _bcd8_increment_inst0_dout; + wire _Counter_inst0_COUT; + reg _GEN_1; + always_comb begin + _GEN_1 = _GEN_0; + if (BTN3) + _GEN_1 = 1'h1; + end // always_comb + reg _GEN_2; + always_comb begin + _GEN_2 = _GEN_1; + if (BTN1) + _GEN_2 = 1'h0; + end // always_comb + reg Register_inst3; + reg [7:0] _GEN_3; + always_comb begin + _GEN_3 = Register_inst0; + if (_Counter_inst0_COUT & Register_inst3) + _GEN_3 = _bcd8_increment_inst0_dout; + end // always_comb + reg [7:0] Register_inst1; + reg [7:0] _GEN_4; + reg [4:0] _GEN_5; + always_comb begin + _GEN_5 = _GEN; + _GEN_4 = Register_inst1; + if (BTN2) begin + _GEN_4 = Register_inst0; + _GEN_5 = 5'h14; + end + end // always_comb + reg [4:0] Register_inst2; + reg [4:0] _GEN_6; + always_comb begin + _GEN_6 = Register_inst2; + if (_Counter_inst0_COUT & (|Register_inst2)) + _GEN_6 = Register_inst2 - 5'h1; + end // always_comb + reg [7:0] _GEN_7; + always_comb begin + _GEN_7 = _GEN_3; + _GEN = _GEN_6; + _GEN_0 = Register_inst3; + if (~BTN_N) begin + _GEN_7 = 8'h0; + _GEN_0 = 1'h0; + _GEN = 5'h0; + end + end // always_comb + always_ff @(posedge CLK) begin + Register_inst3 <= _GEN_2; + Register_inst1 <= _GEN_4; + Register_inst2 <= _GEN_5; + Register_inst0 <= _GEN_7; + end // always_ff @(posedge) + initial begin + Register_inst3 = 1'h0; + Register_inst1 = 8'h0; + Register_inst2 = 5'h0; + Register_inst0 = 8'h0; + end // initial + wire [1:0][7:0] _GEN_8 = {{Register_inst1}, {Register_inst0}}; + Counter Counter_inst0 ( + .CLK (CLK), + .O (/* unused */), + .COUT (_Counter_inst0_COUT) + ); + bcd8_increment bcd8_increment_inst0 ( + .din (Register_inst0), + .dout (_bcd8_increment_inst0_dout) + ); + seven_seg_ctrl seven_seg_ctrl_inst0 ( + .CLK (CLK), + .din (_GEN_8[|Register_inst2]), + .dout (_seven_seg_ctrl_inst0_dout) + ); + assign LED1 = BTN1 & BTN2; + assign LED2 = BTN1 & BTN3; + assign LED3 = BTN2 & BTN3; + assign LED4 = ~BTN_N; + assign LED5 = ~BTN_N | BTN1 | BTN2 | BTN3; + assign P1A1 = _seven_seg_ctrl_inst0_dout[7]; + assign P1A2 = _seven_seg_ctrl_inst0_dout[6]; + assign P1A3 = _seven_seg_ctrl_inst0_dout[5]; + assign P1A4 = _seven_seg_ctrl_inst0_dout[4]; + assign P1A7 = _seven_seg_ctrl_inst0_dout[3]; + assign P1A8 = _seven_seg_ctrl_inst0_dout[2]; + assign P1A9 = _seven_seg_ctrl_inst0_dout[1]; + assign P1A10 = _seven_seg_ctrl_inst0_dout[0]; +endmodule + diff --git a/examples/icebreaker-workshop/stopwatch/test_stopwatch.py b/examples/icebreaker-workshop/stopwatch/test_stopwatch.py new file mode 100644 index 000000000..640c9fe94 --- /dev/null +++ b/examples/icebreaker-workshop/stopwatch/test_stopwatch.py @@ -0,0 +1,9 @@ +import magma as m +from magma.testing.utils import check_gold + +from stopwatch import top + + +def test_stopwatch_compile(): + m.compile("build/stopwatch", top, output="mlir") + assert check_gold(__file__, "stopwatch.mlir")