├── MANIFEST.in ├── chipy ├── __init__.py └── Chipy.py ├── .gitignore ├── setup.py ├── tests ├── test008.py ├── test009.py ├── test005.py ├── test003.py ├── test006.py ├── runtests.sh ├── test000.py ├── test007.py ├── test002.py ├── test004.py └── test001.py └── README.md /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | -------------------------------------------------------------------------------- /chipy/__init__.py: -------------------------------------------------------------------------------- 1 | from chipy.Chipy import * 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | dist/ 3 | *.egg-info/ 4 | *.v 5 | *.log -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | setup( 4 | name='chipy', 5 | packages=find_packages(exclude=['tests']), 6 | version='0.1.1', 7 | description='Chipy is a single-file python module for generating digital hardware.', 8 | long_description=open('README.md').read(), 9 | author='Clifford Wolf', 10 | author_email='clifford@clifford.at', 11 | url='https://github.com/chipy-hdl/chipy', 12 | keywords=['eda', 'cad', 'hdl', 'verilog'], 13 | license='ISC' 14 | ) 15 | -------------------------------------------------------------------------------- /tests/test008.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | with AddModule("gate_1"): 7 | clk = AddInput("clk") 8 | out = AddOutput("out", 32, posedge=clk, initial=18) 9 | out.next = out + 1 10 | 11 | 12 | with AddModule("gate_2"): 13 | clk = AddInput("clk") 14 | out = AddOutput("out", 32) 15 | AddFF(out, posedge=clk, initial=18) 16 | out.next = out + 1 17 | 18 | 19 | with open("test008.v", "w") as f: 20 | print(""" 21 | //@ test-sat-equiv-induct gold gate_1 5 22 | //@ test-sat-equiv-induct gold gate_2 5 23 | """, file=f) 24 | 25 | WriteVerilog(f) 26 | 27 | print(""" 28 | module gold(clk, out); 29 | input clk; 30 | output reg [31:0] out; 31 | initial begin 32 | out = 18; 33 | end 34 | always @(posedge clk) begin 35 | out <= out + 1; 36 | end 37 | endmodule 38 | """, file=f) 39 | -------------------------------------------------------------------------------- /tests/test009.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | with AddModule("gate_1"): 7 | clk = AddInput("clk") 8 | out = AddOutput("out", 1, posedge=clk) 9 | o32 = AddOutput("out32", 32, initial=123456789, posedge=clk) 10 | out.next = Sig(1, 1) 11 | o32.next = o32 12 | 13 | with AddModule("gate_2"): 14 | clk = AddInput("clk") 15 | out = AddOutput("out", 1, posedge=clk) 16 | o32 = AddOutput("out32", 32, async=True) 17 | out.next = ~Sig(0, 1) 18 | o32.next = 123456789 19 | 20 | 21 | with open("test009.v", "w") as f: 22 | print(""" 23 | //@ test-sat-equiv-bmc gold gate_1 5 24 | //@ test-sat-equiv-bmc gold gate_2 5 25 | """, file=f) 26 | 27 | WriteVerilog(f) 28 | 29 | print(""" 30 | module gold(clk, out, out32); 31 | input clk; 32 | output reg out; 33 | output reg [31:0] out32; 34 | always @(posedge clk) begin 35 | out <= 1; 36 | out32 <= 123456789; 37 | end 38 | endmodule 39 | """, file=f) 40 | -------------------------------------------------------------------------------- /tests/test005.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | with AddModule("gate_1"): 7 | clk = AddInput("clk") 8 | addr1, addr2, din = AddInput("addr1 addr2 din", 2) 9 | dout = AddOutput("dout", 8, async=True) 10 | 11 | mem1, mem2 = AddMemory("mem1 mem2", 4, 4, posedge=clk) 12 | 13 | mem1[addr1][addr2].next = din[1] 14 | mem2[addr1][addr2].next = din[0] 15 | 16 | dout.next = Concat([mem1[addr1], mem2[addr2]]) 17 | 18 | 19 | with open("test005.v", "w") as f: 20 | print(""" 21 | //@ test-sat-equiv-bmc gold gate_1 5 22 | """, file=f) 23 | 24 | WriteVerilog(f) 25 | 26 | print(""" 27 | module gold( 28 | input clk, 29 | input [1:0] addr1, addr2, din, 30 | output [7:0] dout 31 | ); 32 | reg [3:0] mem1 [0:3]; 33 | reg [3:0] mem2 [0:3]; 34 | assign dout = {mem1[addr1], mem2[addr2]}; 35 | always @(posedge clk) begin 36 | mem1[addr1][addr2] <= din[1]; 37 | mem2[addr1][addr2] <= din[0]; 38 | end 39 | endmodule 40 | """, file=f) 41 | -------------------------------------------------------------------------------- /tests/test003.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | with AddModule("gate_1"): 7 | clk, wen1, wen2 = AddInput("clk wen1 wen2") 8 | addr1, addr2 = AddInput("addr1 addr2", 2) 9 | rdata1, rdata2 = AddOutput("rdata1 rdata2", 3, async=True) 10 | wdata = AddInput("wdata", 3) 11 | 12 | mem = AddMemory("mem", 3, 4, posedge=clk) 13 | 14 | with If(wen1): 15 | mem[addr1].next = wdata 16 | 17 | with If(wen2): 18 | mem[addr2].next = wdata 19 | 20 | rdata1.next = mem[addr1] 21 | rdata2.next = mem[addr2] 22 | 23 | 24 | with open("test003.v", "w") as f: 25 | print(""" 26 | //@ test-sat-equiv-bmc gold gate_1 5 27 | """, file=f) 28 | 29 | WriteVerilog(f) 30 | 31 | print(""" 32 | module gold( 33 | input clk, wen1, wen2, 34 | input [1:0] addr1, addr2, 35 | output [2:0] rdata1, rdata2, 36 | input [2:0] wdata 37 | ); 38 | reg [2:0] mem [0:3]; 39 | assign rdata1 = mem[addr1]; 40 | assign rdata2 = mem[addr2]; 41 | always @(posedge clk) begin 42 | if (wen1) mem[addr1] <= wdata; 43 | if (wen2) mem[addr2] <= wdata; 44 | end 45 | endmodule 46 | """, file=f) 47 | -------------------------------------------------------------------------------- /tests/test006.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | with AddModule("gate_1"): 7 | sel = AddInput("sel", 2) 8 | din = AddInput("din", 32) 9 | dout = AddOutput("dout", 32, async=True) 10 | 11 | dout.next = 0 12 | 13 | with Switch(sel): 14 | with Case(0): 15 | dout[15:0].next = din[31:16] 16 | dout[31:16].next = din[15:0] 17 | with Case(1): 18 | dout[1].next = din[30] 19 | dout[7].next = ~din[20] 20 | dout[din[3:0]].next = din[din[7:4]] 21 | with Case(2): 22 | dout[10 + din[3:0], +5].next = din[10 + din[7:4], -5] 23 | with Case(3): 24 | dout[10 + din[3:0], -5].next = din[10 + din[7:4], +5] 25 | 26 | 27 | with open("test006.v", "w") as f: 28 | print(""" 29 | //@ test-sat-equiv-comb gold gate_1 30 | """, file=f) 31 | 32 | WriteVerilog(f) 33 | 34 | print(""" 35 | module gold( 36 | input [1:0] sel, 37 | input [31:0] din, 38 | output reg [31:0] dout 39 | ); 40 | always @* begin 41 | dout = 0; 42 | case (sel) 43 | 0: begin 44 | {dout[15:0], dout[31:16]} = din; 45 | end 46 | 1: begin 47 | dout[1] = din[30]; 48 | dout[7] = ~din[20]; 49 | dout[din[3:0]] = din[din[7:4]]; 50 | end 51 | 2: begin 52 | dout[10 + din[3:0] +: 5] = din[10 + din[7:4] -: 5]; 53 | end 54 | 3: begin 55 | dout[10 + din[3:0] -: 5] = din[10 + din[7:4] +: 5]; 56 | end 57 | endcase 58 | end 59 | endmodule 60 | """, file=f) 61 | -------------------------------------------------------------------------------- /tests/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -f test[0-9][0-9][0-9].v 4 | rm -f test[0-9][0-9][0-9]_*.log 5 | 6 | set -e 7 | 8 | run() { 9 | idx=0 id=$id 10 | echo -n "Checking ${id}.." 11 | 12 | PYTHONPATH=".." python3 ${id}.py 13 | 14 | yosys_q="-q" 15 | if $verbose; then 16 | yosys_q="" 17 | fi 18 | 19 | while read tag stmt args; do 20 | (( ++idx )) 21 | read a1 a2 a3 a4 a5 < <( echo $args; ) 22 | case "$stmt" in 23 | test-sat-equiv-induct) 24 | yosys $yosys_q -l ${id}_${idx}.log -p "read_verilog ${id}.v; prep" \ 25 | -p "miter -equiv -ignore_gold_x -flatten -make_outputs $a1 $a2 miter" \ 26 | -p "sat -verify -prove trigger 0 -show-ports -tempinduct -set-init-undef -set-def-inputs -maxsteps $a3 miter" 27 | ;; 28 | test-sat-equiv-bmc) 29 | yosys $yosys_q -l ${id}_${idx}.log -p "read_verilog ${id}.v; prep; memory_map;;" \ 30 | -p "miter -equiv -ignore_gold_x -flatten -make_outputs $a1 $a2 miter" \ 31 | -p "sat -verify -prove trigger 0 -show-ports -set-init-undef -set-def-inputs -seq $a3 miter" 32 | ;; 33 | test-sat-equiv-comb) 34 | yosys $yosys_q -l ${id}_${idx}.log -p "read_verilog ${id}.v; prep; memory_map;;" \ 35 | -p "miter -equiv -ignore_gold_x -flatten -make_outputs $a1 $a2 miter" \ 36 | -p "sat -verify -prove trigger 0 -show-ports -set-def-inputs miter" 37 | ;; 38 | *) 39 | echo "Unsupported test stmt: $tag $stmt $args" 40 | exit 1 41 | ;; 42 | esac 43 | echo -n " ok_$idx" 44 | done < <( grep '^//@' ${id}.v; ) 45 | echo 46 | } 47 | 48 | verbose=false 49 | if [ "$1" = "-v" ]; then 50 | verbose=true 51 | shift 52 | fi 53 | 54 | if [ $# = 0 ]; then 55 | for id in test[0-9][0-9][0-9].py; do 56 | id=${id%.py} 57 | run $id 58 | done 59 | else 60 | for id; do 61 | run $id 62 | done 63 | fi 64 | 65 | echo "ALL OK" 66 | -------------------------------------------------------------------------------- /tests/test000.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | with AddModule("gate_1"): 7 | clk = AddInput("clk") 8 | submode = AddInput("submode") 9 | a, b = AddInput("a b", 32) 10 | out = AddOutput("out", 32, posedge=clk) 11 | 12 | with If(submode): 13 | out.next = a - b 14 | with Else: 15 | out.next = a + b 16 | 17 | 18 | with AddModule("gate_2"): 19 | clk = AddInput("clk") 20 | submode = AddInput("submode") 21 | a, b = AddInput("a b", 32) 22 | out = AddOutput("out", 32) 23 | 24 | AddFF(out, posedge=clk) 25 | 26 | with If(submode): 27 | out.next = a - b 28 | with Else: 29 | out.next = a + b 30 | 31 | 32 | with AddModule("gate_3"): 33 | AddInput("clk") 34 | AddInput("submode") 35 | AddInput("a b", 32) 36 | AddOutput("out", 32) 37 | 38 | AddFF(Sig("out"), posedge=Sig("clk")) 39 | 40 | with If(submode): 41 | Assign(Sig("out"), Sig("a") - Sig("b")) 42 | with Else: 43 | Assign(Sig("out"), Sig("a") + Sig("b")) 44 | 45 | 46 | with AddModule("gate_4"): 47 | clk = AddInput("clk") 48 | submode = AddInput("submode") 49 | a, b = AddInput("a b", 32) 50 | out = AddOutput("out", 32, posedge=clk) 51 | out.next = Cond(submode, a - b, a + b) 52 | 53 | 54 | with open("test000.v", "w") as f: 55 | print(""" 56 | //@ test-sat-equiv-induct gold gate_1 5 57 | //@ test-sat-equiv-induct gold gate_2 5 58 | //@ test-sat-equiv-induct gold gate_3 5 59 | //@ test-sat-equiv-induct gold gate_4 5 60 | """, file=f) 61 | 62 | WriteVerilog(f) 63 | 64 | print(""" 65 | module gold(clk, submode, a, b, out); 66 | input clk, submode; 67 | input [31:0] a, b; 68 | output reg [31:0] out; 69 | 70 | always @(posedge clk) begin 71 | if (submode) 72 | out <= a - b; 73 | else 74 | out <= a + b; 75 | end 76 | endmodule 77 | """, file=f) 78 | -------------------------------------------------------------------------------- /tests/test007.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | with AddModule("gate_1"): 7 | sel = AddInput("sel", 5) 8 | a, b = AddInput("a b", 8) 9 | y = AddOutput("y", 8, async=True) 10 | 11 | with Switch(sel): 12 | with Case( 0): y.next = -a 13 | with Case( 1): y.next = ~a 14 | with Case( 2): y.next = a + b 15 | with Case( 3): y.next = a - b 16 | with Case( 4): y.next = a << b 17 | with Case( 5): y.next = a >> b 18 | with Case( 6): y.next = a & b 19 | with Case( 7): y.next = a | b 20 | with Case( 8): y.next = a ^ b 21 | with Case( 9): y.next = a < b 22 | with Case(10): y.next = a <= b 23 | with Case(11): y.next = a == b 24 | with Case(12): y.next = a != b 25 | with Case(13): y.next = a >= b 26 | with Case(14): y.next = a > b 27 | with Case(15): y.next = a.reduce_and() 28 | with Case(16): y.next = a.reduce_or() 29 | with Case(17): y.next = a.reduce_xor() 30 | with Case(18): y.next = a.logic() 31 | with Case(19): y.next = Repeat(4, a[b[2:0]]) 32 | with Default: y.next = 0 33 | 34 | 35 | with open("test007.v", "w") as f: 36 | print(""" 37 | //@ test-sat-equiv-comb gold gate_1 38 | """, file=f) 39 | 40 | WriteVerilog(f) 41 | 42 | print(""" 43 | module gold( 44 | input [4:0] sel, 45 | input [7:0] a, b, 46 | output reg [7:0] y 47 | ); 48 | always @* begin 49 | case (sel) 50 | 0: y = -a; 51 | 1: y = ~a; 52 | 53 | 2: y = a + b; 54 | 3: y = a - b; 55 | 4: y = a << b; 56 | 5: y = a >> b; 57 | 6: y = a & b; 58 | 7: y = a | b; 59 | 8: y = a ^ b; 60 | 61 | 9: y = a < b; 62 | 10: y = a <= b; 63 | 11: y = a == b; 64 | 12: y = a != b; 65 | 13: y = a >= b; 66 | 14: y = a > b; 67 | 68 | 15: y = &a; 69 | 16: y = |a; 70 | 17: y = ^a; 71 | 18: y = !!a; 72 | 73 | 19: y = {4{a[b[2:0]]}}; 74 | 75 | default: y = 0; 76 | endcase 77 | end 78 | endmodule 79 | """, file=f) 80 | -------------------------------------------------------------------------------- /tests/test002.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | def rgbdata(addport, role): 7 | addport("red", 8) 8 | addport("green", 8) 9 | addport("blue", 8) 10 | 11 | def graydata(addport, role): 12 | addport("gray", 8) 13 | 14 | def rgb2gray(indata): 15 | rgbsum = Sig(indata.red_, 10) + Sig(indata.green_, 10) + Sig(indata.blue_, 10) 16 | return Bundle(gray_=(rgbsum >> 1) - (rgbsum >> 3) - (rgbsum >> 4)) 17 | 18 | 19 | with AddModule("gate_1"): 20 | clk = AddInput("clk") 21 | inp = AddPort("in", Stream(rgbdata), "sink") 22 | out = AddPort("out", Stream(graydata), "source") 23 | 24 | inp.ready_.next = inp.valid_ & (~out.valid_ | out.ready_) 25 | 26 | with If(out.ready_): 27 | out.valid_.next = 0 28 | 29 | with If(inp.ready_): 30 | out.data_.next = rgb2gray(inp.data_) 31 | out.valid_.next = 1 32 | 33 | AddAsync(inp.ready_) 34 | AddFF(out.regs(), posedge=clk) 35 | 36 | 37 | with AddModule("gate_2"): 38 | ports = AddPort("", Module("gate_1").intf(), "child") 39 | inst = AddInst("inst", Module("gate_1")) 40 | Connect([ports, inst]) 41 | 42 | 43 | with open("test002.v", "w") as f: 44 | print(""" 45 | //@ test-sat-equiv-induct gold gate_1 5 46 | //@ test-sat-equiv-induct gold gate_2 5 47 | """, file=f) 48 | 49 | WriteVerilog(f) 50 | 51 | print(""" 52 | module gold( 53 | input clk, 54 | 55 | input [7:0] in__data__red, 56 | input [7:0] in__data__green, 57 | input [7:0] in__data__blue, 58 | input in__valid, 59 | output in__ready, 60 | 61 | output reg [7:0] out__data__gray, 62 | output reg out__valid, 63 | input out__ready 64 | ); 65 | assign in__ready = in__valid && (!out__valid || out__ready); 66 | wire [9:0] rgbsum = in__data__red + in__data__green + in__data__blue; 67 | wire [7:0] gray = (rgbsum >> 1) - (rgbsum >> 3) - (rgbsum >> 4); 68 | 69 | always @(posedge clk) begin 70 | if (out__ready) begin 71 | out__valid <= 0; 72 | end 73 | if (in__ready) begin 74 | out__data__gray <= gray; 75 | out__valid <= 1; 76 | end 77 | end 78 | endmodule 79 | """, file=f) 80 | -------------------------------------------------------------------------------- /tests/test004.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | def abc(addport, role): 7 | addport("a", 2) 8 | addport("b", 2) 9 | addport("c", 2) 10 | 11 | 12 | with AddModule("gate_1"): 13 | clk = AddInput("clk") 14 | waddr, raddra, raddrb, raddrc = AddInput("waddr raddra raddrb raddrc", 2) 15 | inp = AddInput("in", abc) 16 | out = AddOutput("out", abc, async=True) 17 | 18 | mem = AddMemory("mem", abc, 4, posedge=clk) 19 | 20 | mem[waddr].next = inp 21 | 22 | out.a_.next = mem.a_[raddra] 23 | out.b_.next = mem.b_[raddrb] 24 | out.c_.next = mem.c_[raddrc] 25 | 26 | 27 | with AddModule("gate_2"): 28 | clk = AddInput("clk") 29 | waddr, raddra, raddrb, raddrc = AddInput("waddr raddra raddrb raddrc", 2) 30 | inp = AddInput("in", abc) 31 | out = AddOutput("out", abc, async=True) 32 | 33 | mem = AddMemory("mem", abc, 4, posedge=clk) 34 | 35 | mem.a_[waddr].next = inp.a_ 36 | mem.b_[waddr].next = inp.b_ 37 | mem.c_[waddr].next = inp.c_ 38 | 39 | tmp_a = AddReg("tmp_a", abc, async=True) 40 | tmp_b = AddReg("tmp_b", abc, async=True) 41 | tmp_c = AddReg("tmp_c", abc, async=True) 42 | 43 | tmp_a.next = mem[raddra] 44 | tmp_b.next = mem[raddrb] 45 | tmp_c.next = mem[raddrc] 46 | 47 | out.a_.next = tmp_a.a_ 48 | out.b_.next = tmp_b.b_ 49 | out.c_.next = tmp_c.c_ 50 | 51 | 52 | with open("test004.v", "w") as f: 53 | print(""" 54 | //@ test-sat-equiv-bmc gold gate_1 5 55 | //@ test-sat-equiv-bmc gold gate_2 5 56 | """, file=f) 57 | 58 | WriteVerilog(f) 59 | 60 | print(""" 61 | module gold( 62 | input clk, 63 | input [1:0] waddr, raddra, raddrb, raddrc, 64 | input [1:0] in__a, in__b, in__c, 65 | output [1:0] out__a, out__b, out__c, 66 | ); 67 | reg [1:0] mem_a [0:3]; 68 | reg [1:0] mem_b [0:3]; 69 | reg [1:0] mem_c [0:3]; 70 | assign out__a = mem_a[raddra]; 71 | assign out__b = mem_b[raddrb]; 72 | assign out__c = mem_c[raddrc]; 73 | always @(posedge clk) begin 74 | mem_a[waddr] <= in__a; 75 | mem_b[waddr] <= in__b; 76 | mem_c[waddr] <= in__c; 77 | end 78 | endmodule 79 | """, file=f) 80 | -------------------------------------------------------------------------------- /tests/test001.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from chipy import * 4 | 5 | 6 | with AddModule("gate_1"): 7 | clk = AddInput("clk") 8 | reverse_order = AddInput("reverse_order") 9 | concat_mode = AddInput("concat_mode") 10 | a, b = AddInput("a b", 32) 11 | out = AddOutput("out", 32, posedge=clk) 12 | 13 | with If(concat_mode): 14 | with If(reverse_order): 15 | out.next = Concat([b[15:0], a[15:0]]) 16 | with Else: 17 | out.next = Concat([a[15:0], b[15:0]]) 18 | with Else: 19 | with If(reverse_order): 20 | out.next = b - a 21 | with Else: 22 | out.next = a - b 23 | 24 | 25 | with AddModule("gate_2"): 26 | clk = AddInput("clk") 27 | reverse_order = AddInput("reverse_order") 28 | concat_mode = AddInput("concat_mode") 29 | a, b = AddInput("a b", 32) 30 | out = AddOutput("out", 32, posedge=clk) 31 | 32 | aa, bb = AddReg("aa bb", 32, async=True) 33 | 34 | with If(reverse_order): 35 | aa.next = b 36 | bb.next = a 37 | with Else: 38 | aa.next = a 39 | bb.next = b 40 | 41 | with If(concat_mode): 42 | out.next = Concat([aa[15:0], bb[15:0]]) 43 | with Else: 44 | out.next = aa - bb 45 | 46 | 47 | with AddModule("gate_3"): 48 | clk = AddInput("clk") 49 | reverse_order = AddInput("reverse_order") 50 | concat_mode = AddInput("concat_mode") 51 | a, b = AddInput("a b", 32) 52 | out = AddOutput("out", 32, posedge=clk) 53 | 54 | aa, bb = AddReg("aa bb", 32, async=True) 55 | 56 | Concat([aa, bb]).next = Concat([a, b]) 57 | with If(reverse_order): 58 | Concat([aa, bb]).next = Concat([b, a]) 59 | 60 | Assign(out, Cond(concat_mode, Concat([aa[15:0], bb[15:0]]), aa - bb)) 61 | 62 | 63 | with AddModule("gate_4"): 64 | clk = AddInput("clk") 65 | reverse_order = AddInput("reverse_order") 66 | concat_mode = AddInput("concat_mode") 67 | a, b = AddInput("a b", 32) 68 | out = AddOutput("out", 32, posedge=clk) 69 | 70 | with Switch(Concat([concat_mode, reverse_order])): 71 | with Case(0b00): out.next = a - b 72 | with Case(0b01): out.next = b - a 73 | with Case(0b10): out.next = Concat([a[15:0], b[15:0]]) 74 | with Case(0b11): out.next = Concat([b[15:0], a[15:0]]) 75 | 76 | 77 | with open("test001.v", "w") as f: 78 | print(""" 79 | //@ test-sat-equiv-induct gold gate_1 5 80 | //@ test-sat-equiv-induct gold gate_2 5 81 | //@ test-sat-equiv-induct gold gate_3 5 82 | //@ test-sat-equiv-induct gold gate_4 5 83 | """, file=f) 84 | 85 | WriteVerilog(f) 86 | 87 | print(""" 88 | module gold(clk, reverse_order, concat_mode, a, b, out); 89 | input clk, reverse_order, concat_mode; 90 | input [31:0] a, b; 91 | output reg [31:0] out; 92 | 93 | always @(posedge clk) begin 94 | case ({concat_mode, reverse_order}) 95 | 2'b 00: out <= a - b; 96 | 2'b 01: out <= b - a; 97 | 2'b 10: out <= {a[15:0], b[15:0]}; 98 | 2'b 11: out <= {b[15:0], a[15:0]}; 99 | endcase 100 | end 101 | endmodule 102 | """, file=f) 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Chipy -- Constructing Hardware In PYthon 3 | ======================================== 4 | 5 | Chipy is a single-file python module for generating digital hardware. Chipy 6 | provides a simple and clean API for writing Verilog code generators. Structural 7 | and behavioral circuit modelling is supported. 8 | 9 | 10 | A Simple Example 11 | ================ 12 | 13 | The following is a simple Chipy example design: 14 | 15 | ```python 16 | from Chipy import * 17 | 18 | with AddModule("ADD_OR_SUB_DEMO"): 19 | clk = AddInput("CLK") 20 | sub = AddInput("SUB") 21 | a, b = AddInput("A B", 32) 22 | out = AddOutput("OUT", 32, posedge=clk) 23 | 24 | with If(sub): 25 | out.next = a - b 26 | with Else: 27 | out.next = a + b 28 | 29 | with open("demo.v", "w") as f: 30 | WriteVerilog(f) 31 | ``` 32 | 33 | The `AddModule` function adds a new module to the design and return it. To 34 | add elements to a module, create a `with : ...` block and call the 35 | corresponding `Add...` functions from within that block. 36 | 37 | Many functions and expressions in Chipy code return a *signal*. E.g. 38 | `AddInput(..)`, `AddOutput(..)`, or `a + b` in the above code. Some 39 | signals are *registers*, which just means that they can be assigned 40 | to, either by calling `Assign(, )` or by assigning 41 | to the `.next` attribute, as demonstrated in the code above. 42 | 43 | Registers also need a synchronisation element, such as a FF, assigned to them. 44 | This can either be done by calling functions such as `AddFF(..)`, or in simple 45 | cases using keyword arguments such as `posedge=` when creating 46 | the register itself. 47 | 48 | Registers that are not assigned a value and/or do not have a synchronization 49 | element will cause a runtime error in `WriteVerilog`. 50 | 51 | Finally assignments can be conditional, enabling behavioral modelling. This is 52 | done by putting the assignments in blocks such as `with If(..): ...` or 53 | `with Else:`. 54 | 55 | Here is a different version of the design, demonstrating some of the variations 56 | mentioned so far: 57 | 58 | ```python 59 | from Chipy import * 60 | 61 | with AddModule("ADD_OR_SUB_DEMO"): 62 | clk = AddInput("CLK") 63 | sub = AddInput("SUB") 64 | a, b = AddInput("A B", 32) 65 | out = AddOutput("OUT", 32) 66 | 67 | with If(sub): 68 | Assign(out, a - b) 69 | with Else: 70 | Assign(out, a + b) 71 | 72 | AddFF(out, posedge=clk) 73 | 74 | with open("demo.v", "w") as f: 75 | WriteVerilog(f) 76 | ``` 77 | 78 | Chipy Reference Manual 79 | ====================== 80 | 81 | Chipy maintains a global design state that contains a set of (Verilog/RTL) 82 | modules and a stack of design contexts. The Chipy `Add*` functions are used to 83 | add elements to the design in memory. Chipy APIs that are used with the Python 84 | `with` statement are used to maintain the stack of design contexts. The current 85 | context determines for example to which module a new instance or wire should be 86 | added. So for example, the `AddInput` function does not have a parameter that 87 | tells it to which module to add a new input port. Instead the input port is 88 | added to the module referenced to by the current context. 89 | 90 | 91 | Creating modules and generating Verilog 92 | --------------------------------------- 93 | 94 | ### AddModule(name) 95 | 96 | This function adds a new module to the design. The module created by this function 97 | is returned. A Python `with` block using a Chipy module as argument is used to 98 | create a new Chipy context that can be used to add elements to the module. For 99 | example, the following will create a new module `demo` with an input port `clk`: 100 | 101 | ```python 102 | demo_mod = AddModule("demo") 103 | 104 | with demo_mod: 105 | AddInput("clk") 106 | ``` 107 | 108 | ### Module(name=None) 109 | 110 | This functions looks up the module with the specified name. If no such module 111 | is found, `None` is returned. If the name parameter is omitted then the module 112 | referenced by the current context is returned. 113 | 114 | ### WriteVerilog(f) 115 | 116 | This function write the current design to the specified file handle. The file 117 | has to be opened first using for example the Python `open` function: 118 | 119 | ```python 120 | with open("demo.v", "w") as f: 121 | WriteVerilog(f) 122 | ``` 123 | 124 | ### ResetDesign() 125 | 126 | This function resets the global Chipy state, e.g. for when multiple designs are 127 | created from one Python script. 128 | 129 | 130 | Adding inputs and outputs 131 | ------------------------- 132 | 133 | ### AddInput(name, type=1) 134 | 135 | This function adds a new input port to the current module. The new signal is 136 | returned. If name contains more than one white-space separated token, then 137 | multiple ports are created at once and a list is returned. For example: 138 | 139 | ```python 140 | with AddModule("demo"): 141 | clk, a, b = AddInput("clk a b") 142 | ``` 143 | 144 | The `type` argument specifies the width of the new signal. A negative number 145 | denotes a signed signal, i.e. the value `5` would be used to create an unsigned 146 | 5 bit wide signal, and the value `-5` would be used to create a signed 5 bit 147 | wide signal. 148 | 149 | Instead of an integer, an *interface* (see below) can be passed as `type` for 150 | the new signal. In that case multiple input ports are generated, as specified by 151 | the interface, and a *bundle* (see blow) of those signals is returned. 152 | 153 | ### AddOutput(name, type=1, posedge=None, negedge=None, nodefault=False, async=False) 154 | 155 | Like `AddInput`, but adds and output port. The signals returned by this functions 156 | are *registers*, i.e. they have a `.next` member that can be assigned to. 157 | 158 | The keyword arguments `posedge`, `negedge`, and `nodefault` cause `AddOuput` to 159 | automatically call `AddFF` (see below) on the generated registers. Similarly, 160 | `async=True` causes `AddOuput` to call `AddAsync` (see below) on the generated 161 | registers. 162 | 163 | Registers and synchronization elements 164 | -------------------------------------- 165 | 166 | ### AddReg(name, type=1, posedge=None, negedge=None, nodefault=False, async=None) 167 | ### AddFF(signal, posedge=None, negedge=None, nodefault=False) 168 | ### AddAsync(signal) 169 | ### Assign(lhs, rhs) 170 | 171 | Signals and expessions 172 | ---------------------- 173 | 174 | ### Sig(arg, width=None) 175 | ### Sig Operators 176 | ### Cond(cond, if\_val, else\_val) 177 | ### Concat(args) 178 | ### Repeat(num, sig) 179 | 180 | Bundles 181 | ------- 182 | 183 | ### Bundle(arg=None, \*\*kwargs) 184 | ### Bundle.add(self, name, member) 185 | ### Bundle.get(name) 186 | ### Bundle.regs() and Bundle.regs() 187 | ### Bundle.keys(), Bundle.values(), Bundle.items() 188 | ### Zip(bundles, recursive=False) 189 | ### Module.bundle(self, prefix="") 190 | 191 | Interfaces 192 | ---------- 193 | 194 | ### AddPort(name, type, role, posedge=None, negedge=None, nodefault=False, async=None) 195 | ### Module.intf(self, prefix="") 196 | ### Stream(data\_type, last=False, destbits=0) 197 | 198 | Memories 199 | -------- 200 | 201 | ### AddMemory(name, type, depth, posedge=None, negedge=None) 202 | ### Memory read and write 203 | ### Memory bundles 204 | 205 | Hierarchical Designs 206 | -------------------- 207 | 208 | ### AddInst(name, type) 209 | ### Connect(sigs) 210 | 211 | Behavioral Modelling 212 | -------------------- 213 | 214 | ### If, ElseIf, Else 215 | ### Switch, Case, Default 216 | 217 | Todos 218 | ===== 219 | 220 | - Complete documentation 221 | - More testcases / examples 222 | - Improved error reporting 223 | - Bundles: flat, unflat, Map, concat 224 | - Verilog Primitive Inst 225 | - Backbox modules 226 | - Label(name, sig) 227 | 228 | License 229 | ======= 230 | 231 | Chipy -- Constructing Hardware In PYthon 232 | 233 | Copyright (C) 2016 Clifford Wolf 234 | 235 | Permission to use, copy, modify, and/or distribute this software for any 236 | purpose with or without fee is hereby granted, provided that the above 237 | copyright notice and this permission notice appear in all copies. 238 | 239 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 240 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 241 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 242 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 243 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 244 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 245 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 246 | -------------------------------------------------------------------------------- /chipy/Chipy.py: -------------------------------------------------------------------------------- 1 | # 2 | # Chipy -- Constructing Hardware In PYthon 3 | # 4 | # Copyright (C) 2016 Clifford Wolf 5 | # 6 | # Permission to use, copy, modify, and/or distribute this software for any 7 | # purpose with or without fee is hereby granted, provided that the above 8 | # copyright notice and this permission notice appear in all copies. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | # 18 | 19 | 20 | import traceback 21 | import os.path 22 | 23 | 24 | ChipySystemVerilog = False 25 | ChipyModulesDict = dict() 26 | ChipyCurrentContext = None 27 | ChipyElseContext = None 28 | ChipyIdCounter = 0 29 | 30 | 31 | def ResetDesign(): 32 | global ChipyModulesDict, ChipyIdCounter, ChipyElseContext 33 | assert ChipyCurrentContext is None 34 | ChipyModulesDict = dict() 35 | ChipyElseContext = None 36 | ChipyIdCounter = 0 37 | 38 | 39 | def ChipyError(message): 40 | print() 41 | print("----------------------------") 42 | print(message) 43 | print("----------------------------") 44 | print() 45 | assert 0 46 | 47 | 48 | def ChipySameModule(modules): 49 | reference = None 50 | 51 | for mod in modules: 52 | if mod is None: continue 53 | if reference is None: reference = mod 54 | assert reference is mod 55 | 56 | if reference is None: 57 | assert ChipyCurrentContext is not None 58 | return ChipyCurrentContext.module 59 | 60 | if ChipyCurrentContext is not None: 61 | assert ChipyCurrentContext.module is reference 62 | 63 | return reference 64 | 65 | 66 | def ChipyAutoName(): 67 | global ChipyIdCounter 68 | ChipyIdCounter += 1 69 | return "__%d" % ChipyIdCounter 70 | 71 | 72 | def ChipyCodeLoc(): 73 | stack = traceback.extract_stack() 74 | 75 | for frame in reversed(stack): 76 | filename = os.path.basename(frame[0]) 77 | lineno = frame[1] 78 | 79 | if filename != "Chipy.py": 80 | return "%s:%d" % (filename, lineno) 81 | 82 | return "Unkown location" 83 | 84 | 85 | class ChipyContext: 86 | def __init__(self, newmod=None): 87 | global ChipyCurrentContext 88 | self.parent = ChipyCurrentContext 89 | ChipyCurrentContext = self 90 | 91 | if newmod is None: 92 | self.module = self.parent.module 93 | self.snippet = self.parent.snippet 94 | 95 | else: 96 | self.module = newmod 97 | self.snippet = None 98 | 99 | def add_line(self, line, lvalues=None): 100 | if self.snippet is None: 101 | self.snippet = ChipySnippet() 102 | self.module.code_snippets.append(self.snippet) 103 | 104 | if lvalues is not None: 105 | self.snippet.lvalue_signals.update(lvalues) 106 | 107 | self.snippet.text_lines.append(self.snippet.indent_str + line) 108 | 109 | def add_indent(self): 110 | self.snippet.indent_str += " " 111 | 112 | def remove_indent(self): 113 | self.snippet.indent_str = self.snippet.indent_str[2:] 114 | 115 | def popctx(self): 116 | global ChipyCurrentContext 117 | ChipyCurrentContext = self.parent 118 | self.parent = None 119 | 120 | def pushctx(self): 121 | global ChipyCurrentContext 122 | assert self.parent is None 123 | self.parent = ChipyCurrentContext 124 | ChipyCurrentContext = self 125 | 126 | 127 | class ChipySnippet: 128 | def __init__(self): 129 | self.indent_str = " " 130 | self.text_lines = list() 131 | self.lvalue_signals = dict() 132 | 133 | 134 | class ChipyModule: 135 | def __init__(self, name): 136 | self.name = name 137 | self.signals = dict() 138 | self.memories = dict() 139 | self.regactions = list() 140 | self.instances = list() 141 | self.codeloc = ChipyCodeLoc() 142 | 143 | self.initial_snippets = list() 144 | self.init_snippets = list() 145 | self.code_snippets = list() 146 | 147 | assert name not in ChipyModulesDict 148 | ChipyModulesDict[name] = self 149 | 150 | def intf(self, prefix=""): 151 | def callback(addport, role): 152 | for signame, signal in sorted(self.signals.items()): 153 | if (signal.inport or signal.outport) and signame.startswith(prefix): 154 | output = False 155 | if signal.inport and role == "parent": output = True 156 | if signal.outport and role == "child": output = True 157 | addport(signame[len(prefix):], signal.width, output=output) 158 | return callback 159 | 160 | def bundle(self, prefix=""): 161 | ret = Bundle() 162 | for signame, signal in sorted(self.signals.items()): 163 | if signame.startswith(prefix): 164 | ret.add(signame[len(prefix):], signal) 165 | return ret 166 | 167 | def write_verilog(self, f): 168 | portlist = list() 169 | wirelist = list() 170 | assignlist = list() 171 | instance_lines = list() 172 | 173 | for memname, memory in sorted(self.memories.items()): 174 | wirelist.append(" %sreg [%d:0] %s [0:%d]; // %s" 175 | % ("signed " if memory.signed else "", 176 | memory.width-1, memory.name, 177 | memory.depth-1, memory.codeloc)) 178 | 179 | for signame, signal in sorted(self.signals.items()): 180 | if not signal.materialize: 181 | continue 182 | if signal.inport or signal.outport: 183 | port_type = "inout" 184 | if not signal.inport: port_type = "output" 185 | if not signal.outport: port_type = "input" 186 | if signal.signed: port_type = "signed " + port_type 187 | if signal.vlog_reg: port_type = port_type + " reg" 188 | if signal.width > 1: 189 | portlist.append(" %s [%d:0] %s /* %s */" 190 | % (port_type, signal.width-1, signal.name, 191 | signal.codeloc)) 192 | else: 193 | portlist.append(" %s %s /* %s */" 194 | % (port_type, signal.name, signal.codeloc)) 195 | else: 196 | wire_type = "wire" 197 | if signal.vlog_reg: wire_type = "reg" 198 | if signal.width > 1: 199 | wirelist.append(" %s [%d:0] %s; // %s" 200 | % (wire_type, signal.width-1, signal.name, 201 | signal.codeloc)) 202 | else: 203 | wirelist.append(" %s %s; // %s" 204 | % (wire_type, signal.name, signal.codeloc)) 205 | if signal.vlog_rvalue is not None: 206 | assignlist.append(" assign %s = %s; // %s" 207 | % (signal.name, signal.vlog_rvalue, 208 | signal.codeloc)) 209 | if signal.register: 210 | if not signal.gotassign: 211 | ChipyError("Register without assignment: %s.%s" 212 | % (signal.module.name, signal.name)) 213 | if not signal.regaction: 214 | ChipyError("Register without synchronization element: %s.%s" 215 | % (signal.module.name, signal.name)) 216 | if signal.width > 1: 217 | wirelist.append(" reg [%d:0] %s; // %s" 218 | % (signal.width-1, signal.vlog_lvalue, 219 | signal.codeloc)) 220 | else: 221 | wirelist.append(" reg %s; // %s" 222 | % (signal.vlog_lvalue, signal.codeloc)) 223 | 224 | for inst_name, inst_type, inst_bundle, inst_codeloc in self.instances: 225 | instance_lines.append(" %s %s ( // %s" 226 | % (inst_type, inst_name, inst_codeloc)) 227 | for member_name, member_sig in inst_bundle.items(): 228 | if member_sig.portalias is None: 229 | expr = member_sig.name 230 | else: 231 | expr = member_sig.portalias 232 | instance_lines.append(" .%s(%s)," % (member_name, expr)) 233 | instance_lines[-1] = instance_lines[-1][:-1] 234 | instance_lines.append(" );") 235 | 236 | print("", file=f) 237 | print("module %s (" % self.name, file=f) 238 | print(",\n".join(portlist), file=f) 239 | print(");", file=f) 240 | 241 | if not ChipySystemVerilog: 242 | print(" reg __always_comb_guard;", file=f) 243 | print(" initial __always_comb_guard <= 1;", file=f) 244 | 245 | for line in wirelist: 246 | print(line, file=f) 247 | 248 | for line in assignlist: 249 | print(line, file=f) 250 | 251 | print(" initial begin", file=f) 252 | for snippet in self.initial_snippets: 253 | for line in snippet.text_lines: 254 | print(line, file=f) 255 | print(" end", file=f) 256 | 257 | snippet_db = self.init_snippets + self.code_snippets 258 | snippet_parent = list() 259 | lvalue_idx = dict() 260 | 261 | def UnionFind_Find(idx): 262 | if snippet_parent[idx] != idx: 263 | snippet_parent[idx] = UnionFind_Find(snippet_parent[idx]) 264 | return snippet_parent[idx] 265 | 266 | def UnionFind_Union(idx1, idx2): 267 | idx1 = UnionFind_Find(idx1) 268 | idx2 = UnionFind_Find(idx2) 269 | snippet_parent[idx1] = idx2 270 | 271 | for idx in range(len(snippet_db)): 272 | snippet_parent.append(idx) 273 | for lval in snippet_db[idx].lvalue_signals.keys(): 274 | if lval not in lvalue_idx: 275 | lvalue_idx[lval] = idx 276 | else: 277 | UnionFind_Union(idx, lvalue_idx[lval]) 278 | 279 | snippet_groups = dict() 280 | 281 | for idx in range(len(snippet_db)): 282 | grp = UnionFind_Find(idx) 283 | if grp not in snippet_groups: 284 | snippet_groups[grp] = list() 285 | snippet_groups[grp].append(snippet_db[idx]) 286 | 287 | for snippets in snippet_groups.values(): 288 | if ChipySystemVerilog: 289 | print(" always_comb begin", file=f) 290 | else: 291 | print(" always @* if (__always_comb_guard) begin", file=f) 292 | for snippet in snippets: 293 | # print(" // -- %s --" % (" ".join([sig.name for sig in snippet.lvalue_signals.keys()])), file=f) 294 | for line in snippet.text_lines: 295 | print(line, file=f) 296 | print(" end", file=f) 297 | 298 | for line in self.regactions: 299 | print(line, file=f) 300 | 301 | for line in instance_lines: 302 | print(line, file=f) 303 | 304 | for memory in self.memories.values(): 305 | if memory.posedge is not None: 306 | print(" always%s @(posedge %s) begin" 307 | % ("_ff" if ChipySystemVerilog else "", 308 | memory.posedge.name), file=f) 309 | if memory.negedge is not None: 310 | print(" always%s @(negedge %s) begin" 311 | % ("_ff" if ChipySystemVerilog else "", 312 | memory.negedge.name), file=f) 313 | for line in memory.regactions: 314 | print(" " + line, file=f) 315 | print(" end", file=f) 316 | 317 | print("endmodule", file=f) 318 | 319 | def __enter__(self): 320 | ChipyContext(newmod=self) 321 | 322 | def __exit__(self, type, value, traceback): 323 | ChipyCurrentContext.popctx() 324 | 325 | 326 | def ChipyUnaryOp(vlogop, a, signprop=True, logicout=False): 327 | a = Sig(a) 328 | 329 | module = ChipySameModule([a.module]) 330 | signal = ChipySignal(module) 331 | 332 | signal.signed = a.signed and signprop 333 | if not logicout: 334 | signal.width = a.width 335 | 336 | signal.vlog_rvalue = "%s %s" % (vlogop, a.name) 337 | signal.deps[a.name] = a 338 | 339 | return signal 340 | 341 | 342 | def ChipyBinaryOp(vlogop, a, b, signprop=True, leftwidth=False): 343 | a = Sig(a) 344 | b = Sig(b) 345 | 346 | module = ChipySameModule([a.module, b.module]) 347 | signal = ChipySignal(module) 348 | 349 | if leftwidth: 350 | signal.width = a.width 351 | signal.signed = a.signed and signprop 352 | else: 353 | signal.width = max(a.width, b.width) 354 | signal.signed = a.signed and b.signed and signprop 355 | 356 | signal.vlog_rvalue = "%s %s %s" % (a.name, vlogop, b.name) 357 | signal.deps[a.name] = a 358 | signal.deps[b.name] = b 359 | 360 | return signal 361 | 362 | 363 | def ChipyCmpOp(vlogop, a, b): 364 | a = Sig(a) 365 | b = Sig(b) 366 | 367 | module = ChipySameModule([a.module, b.module]) 368 | signal = ChipySignal(module) 369 | 370 | signal.vlog_rvalue = "%s %s %s" % (a.name, vlogop, b.name) 371 | signal.deps[a.name] = a 372 | signal.deps[b.name] = b 373 | 374 | return signal 375 | 376 | 377 | class ChipySignal: 378 | def __init__(self, module, name=None, const=False): 379 | if name is None: 380 | name = ChipyAutoName() 381 | 382 | self.name = name 383 | self.module = module 384 | self.codeloc = ChipyCodeLoc() 385 | self.width = 1 386 | self.signed = False 387 | self.register = False 388 | self.regaction = False 389 | self.inport = False 390 | self.outport = False 391 | self.vlog_rvalue = None 392 | self.vlog_lvalue = None 393 | self.vlog_reg = False 394 | self.memory = None 395 | self.materialize = False 396 | self.gotassign = False 397 | self.portalias = None 398 | self.deps = dict() 399 | 400 | if not const: 401 | assert name not in module.signals 402 | module.signals[name] = self 403 | 404 | def get_deps(self): 405 | deps = {self.name: self} 406 | for dep in self.deps.values(): 407 | deps.update(dep.get_deps()) 408 | return deps 409 | 410 | def set_materialize(self): 411 | if self.materialize: return 412 | self.materialize = True 413 | for dep in self.deps.values(): 414 | dep.set_materialize() 415 | 416 | def __setattr__(self, name, value): 417 | if name == "next": 418 | Assign(self, value) 419 | else: 420 | super().__setattr__(name, value) 421 | 422 | def __getitem__(self, index): 423 | if self.memory is None: 424 | self_name = self.name 425 | self_deps = {self.name: self} 426 | else: 427 | self_name = self.vlog_rvalue 428 | self_deps = dict() 429 | 430 | if isinstance(index, tuple): 431 | index, width = index 432 | assert not isinstance(index, slice) 433 | assert isinstance(width, int) 434 | 435 | updown = "+" if width >= 0 else "-" 436 | width = abs(width) 437 | 438 | signal = ChipySignal(self.module) 439 | signal.memory = self.memory 440 | signal.width = width 441 | signal.deps.update(self_deps) 442 | 443 | if isinstance(index, ChipySignal): 444 | index.set_materialize() 445 | index = index.name 446 | elif isinstance(index, int): 447 | index = "%d" % index 448 | else: 449 | assert 0 450 | 451 | signal.vlog_rvalue = "%s[%s %c: %d]" \ 452 | % (self_name, index, updown, width) 453 | if self.vlog_lvalue is not None: 454 | signal.vlog_lvalue = "%s[%s %c: %d]" \ 455 | % (self.vlog_lvalue, index, updown, width) 456 | return signal 457 | 458 | if isinstance(index, slice): 459 | msb = max(index.start, index.stop) 460 | lsb = min(index.start, index.stop) 461 | 462 | signal = ChipySignal(self.module) 463 | signal.memory = self.memory 464 | signal.width = msb - lsb + 1 465 | signal.deps.update(self_deps) 466 | 467 | signal.vlog_rvalue = "%s[%d:%d]" % (self_name, msb, lsb) 468 | if self.vlog_lvalue is not None: 469 | signal.vlog_lvalue = "%s[%d:%d]" % (self.vlog_lvalue, msb, lsb) 470 | 471 | return signal 472 | 473 | if isinstance(index, ChipySignal): 474 | signal = ChipySignal(self.module) 475 | signal.memory = self.memory 476 | signal.width = 1 477 | signal.deps.update(self_deps) 478 | 479 | signal.vlog_rvalue = "%s[%s]" % (self_name, index.name) 480 | if self.vlog_lvalue is not None: 481 | signal.vlog_lvalue = "%s[%s]" % (self.vlog_lvalue, index.name) 482 | 483 | index.set_materialize() 484 | return signal 485 | 486 | if isinstance(index, int): 487 | signal = ChipySignal(self.module) 488 | signal.memory = self.memory 489 | signal.width = 1 490 | signal.deps.update(self_deps) 491 | 492 | signal.vlog_rvalue = "%s[%d]" % (self_name, index) 493 | if self.vlog_lvalue is not None: 494 | signal.vlog_lvalue = "%s[%d]" % (self.vlog_lvalue, index) 495 | 496 | return signal 497 | 498 | assert 0 499 | 500 | def __neg__(self): 501 | return ChipyUnaryOp("-", self) 502 | 503 | def __invert__(self): 504 | return ChipyUnaryOp("~", self) 505 | 506 | def __add__(self, other): 507 | return ChipyBinaryOp("+", self, other) 508 | 509 | def __radd__(self, other): 510 | return ChipyBinaryOp("+", other, self) 511 | 512 | def __sub__(self, other): 513 | return ChipyBinaryOp("-", self, other) 514 | 515 | def __rsub__(self, other): 516 | return ChipyBinaryOp("-", other, self) 517 | 518 | def __mul__(self, other): 519 | return ChipyBinaryOp("*", self, other) 520 | 521 | def __rmul__(self, other): 522 | return ChipyBinaryOp("*", other, self) 523 | 524 | def __floordiv__(self, other): 525 | return ChipyBinaryOp("/", self, other) 526 | 527 | def __rfloordiv__(self, other): 528 | return ChipyBinaryOp("/", other, self) 529 | 530 | def __mod__(self, other): 531 | return ChipyBinaryOp("%", self, other) 532 | 533 | def __rmod__(self, other): 534 | return ChipyBinaryOp("%", other, self) 535 | 536 | def __pow__(self, other): 537 | return ChipyBinaryOp("**", self, other) 538 | 539 | def __rpow__(self, other): 540 | return ChipyBinaryOp("**", other, self) 541 | 542 | def __lshift__(self, other): 543 | return ChipyBinaryOp("<<<", self, other, leftwidth=True) 544 | 545 | def __rlshift__(self, other): 546 | return ChipyBinaryOp("<<<", other, self, leftwidth=True) 547 | 548 | def __rshift__(self, other): 549 | return ChipyBinaryOp(">>>", self, other, leftwidth=True) 550 | 551 | def __rrshift__(self, other): 552 | return ChipyBinaryOp(">>>", other, self, leftwidth=True) 553 | 554 | def __and__(self, other): 555 | return ChipyBinaryOp("&", self, other) 556 | 557 | def __rand__(self, other): 558 | return ChipyBinaryOp("&", other, self) 559 | 560 | def __xor__(self, other): 561 | return ChipyBinaryOp("^", self, other) 562 | 563 | def __rxor__(self, other): 564 | return ChipyBinaryOp("^", other, self) 565 | 566 | def __or__(self, other): 567 | return ChipyBinaryOp("|", self, other) 568 | 569 | def __ror__(self, other): 570 | return ChipyBinaryOp("|", other, self) 571 | 572 | def __lt__(self, other): 573 | return ChipyCmpOp("<", self, other) 574 | 575 | def __le__(self, other): 576 | return ChipyCmpOp("<=", self, other) 577 | 578 | def __eq__(self, other): 579 | return ChipyCmpOp("==", self, other) 580 | 581 | def __ne__(self, other): 582 | return ChipyCmpOp("!=", self, other) 583 | 584 | def __gt__(self, other): 585 | return ChipyCmpOp(">", self, other) 586 | 587 | def __ge__(self, other): 588 | return ChipyCmpOp(">=", self, other) 589 | 590 | def reduce_and(self): 591 | return ChipyUnaryOp("&", self, signprop=False, logicout=True) 592 | 593 | def reduce_or(self): 594 | return ChipyUnaryOp("|", self, signprop=False, logicout=True) 595 | 596 | def reduce_xor(self): 597 | return ChipyUnaryOp("^", self, signprop=False, logicout=True) 598 | 599 | def logic(self): 600 | return ChipyUnaryOp("|", self, signprop=False, logicout=True) 601 | 602 | 603 | class ChipyMemory: 604 | def __init__(self, module, width, depth, name=None, posedge=None, 605 | negedge=None, signed=False): 606 | if name is None: 607 | name = ChipyAutoName() 608 | 609 | assert (posedge is None) != (negedge is None) 610 | 611 | self.name = name 612 | self.module = module 613 | self.codeloc = ChipyCodeLoc() 614 | self.width = width 615 | self.depth = depth 616 | self.posedge = posedge 617 | self.negedge = negedge 618 | self.signed = signed 619 | self.regactions = list() 620 | 621 | assert name not in module.memories 622 | module.memories[name] = self 623 | 624 | def __getitem__(self, index): 625 | index = Sig(index) 626 | signal = ChipySignal(self.module) 627 | signal.width = self.width 628 | signal.vlog_rvalue = "%s[%s]" % (self.name, index.name) 629 | signal.deps.update(index.deps) 630 | signal.memory = self 631 | return signal 632 | 633 | 634 | class ChipyBundle: 635 | def __init__(self): 636 | self.members = dict() 637 | 638 | def add(self, name, member): 639 | self.members[name] = member 640 | 641 | def regs(self): 642 | bundle = ChipyBundle() 643 | for name, member in self.members.items(): 644 | if isinstance(member, ChipyBundle): 645 | bundle.add(name, member.regs()) 646 | elif member.register: 647 | bundle.add(name, member) 648 | return bundle 649 | 650 | def nonregs(self): 651 | bundle = ChipyBundle() 652 | for name, member in self.members.items(): 653 | if isinstance(member, ChipyBundle): 654 | bundle.add(name, member.nonregs()) 655 | elif not member.register: 656 | bundle.add(name, member) 657 | return bundle 658 | 659 | def keys(self): 660 | return self.members.keys() 661 | 662 | def values(self): 663 | return self.members.values() 664 | 665 | def items(self): 666 | return self.members.items() 667 | 668 | def get(self, name): 669 | assert name in self.members 670 | return self.members[name] 671 | 672 | def __getitem__(self, index): 673 | bundle = Bundle() 674 | for key in self.keys(): 675 | bundle.add(key, self.get(key)[index]) 676 | return bundle 677 | 678 | def __setattr__(self, name, value): 679 | if name == "next": 680 | Assign(self, value) 681 | else: 682 | super().__setattr__(name, value) 683 | 684 | def __getattr__(self, name): 685 | if name.endswith("_") and name[:-1] in self.members: 686 | return self.members[name[:-1]] 687 | raise AttributeError 688 | 689 | 690 | def Bundle(arg=None, **kwargs): 691 | bundle = ChipyBundle() 692 | 693 | if arg is not None: 694 | for name, member in arg.items(): 695 | bundle.add(name, member) 696 | 697 | for name, member in kwargs.items(): 698 | assert name.endswith("_") 699 | bundle.add(name[:-1], member) 700 | 701 | return bundle 702 | 703 | 704 | def Zip(bundles, recursive=False): 705 | if isinstance(bundles, (list, tuple)): 706 | list_mode = True 707 | bundles_list = bundles 708 | bundles_keys = range(len(bundles)) 709 | else: 710 | list_mode = False 711 | bundles_list = list(bundles.values()) 712 | bundles_keys = bundles.keys() 713 | 714 | ret = dict() 715 | 716 | if len(bundles_list) == 0: 717 | return ret 718 | 719 | for bundle in bundles_list: 720 | assert bundles_list[0].members.keys() == bundle.members.keys() 721 | 722 | for name in bundles_list[0].members.keys(): 723 | if list_mode: 724 | value = [None] * len(bundles) 725 | else: 726 | value = dict() 727 | 728 | for key in bundles_keys: 729 | value[key] = bundles[key][name] 730 | 731 | ret[name] = value 732 | 733 | return ret 734 | 735 | 736 | def Module(name=None): 737 | if name is None: 738 | assert ChipyCurrentContext is not None 739 | return ChipyCurrentContext.module 740 | if name in ChipyModulesDict: 741 | return ChipyModulesDict[name] 742 | return None 743 | 744 | 745 | def AddModule(name): 746 | return ChipyModule(name) 747 | 748 | 749 | def AddInput(name, type=1): 750 | names = name.split() 751 | if len(names) > 1: 752 | return [AddInput(n, type) for n in names] 753 | assert len(names) == 1 754 | name = names[0] 755 | 756 | if not isinstance(type, int): 757 | return AddPort(name, type, "input") 758 | 759 | assert ChipyCurrentContext is not None 760 | module = ChipyCurrentContext.module 761 | 762 | signal = ChipySignal(module, name) 763 | signal.width = abs(type) 764 | signal.signed = type < 0 765 | signal.inport = True 766 | signal.set_materialize() 767 | return signal 768 | 769 | 770 | def AddOutput(name, type=1, posedge=None, negedge=None, nodefault=False, 771 | async=False, initial=None): 772 | names = name.split() 773 | if len(names) > 1: 774 | outputs = [] 775 | for n in names: 776 | outputs.append(AddOutput(n, type, posedge, negedge, nodefault, async)) 777 | return outputs 778 | assert len(names) == 1 779 | name = names[0] 780 | 781 | if not isinstance(type, int): 782 | return AddPort(name, type, "output", posedge=posedge, negedge=negedge, 783 | nodefault=nodefault, async=async) 784 | 785 | assert ChipyCurrentContext is not None 786 | module = ChipyCurrentContext.module 787 | 788 | signal = ChipySignal(module, name) 789 | signal.width = abs(type) 790 | signal.signed = type < 0 791 | signal.outport = True 792 | signal.register = True 793 | signal.vlog_lvalue = "__next__" + name 794 | signal.set_materialize() 795 | 796 | if posedge is not None or negedge is not None: 797 | AddFF(signal, posedge=posedge, negedge=negedge, nodefault=nodefault, 798 | initial=initial) 799 | 800 | if async: 801 | AddAsync(signal) 802 | 803 | return signal 804 | 805 | 806 | def AddPort(name, type, role, posedge=None, negedge=None, nodefault=False, 807 | async=None): 808 | bundle = ChipyBundle() 809 | 810 | def addport(port_name, port_type, port_role=None, output=False): 811 | if role in ("input", "output", "register"): 812 | port_role = role 813 | 814 | if port_role in ("input", "output"): 815 | output = (port_role == "output") 816 | 817 | if port_role is None: 818 | port_role = "output" if output else "input" 819 | 820 | prefix = (name + "__") if name != "" else "" 821 | 822 | if isinstance(port_type, int): 823 | if role == "register": 824 | reg = AddReg(prefix + port_name, port_type, posedge=posedge, 825 | negedge=negedge, nodefault=nodefault, async=async) 826 | bundle.add(port_name, reg) 827 | elif output: 828 | out = AddOutput(prefix + port_name, port_type, posedge=posedge, 829 | negedge=negedge, nodefault=nodefault, 830 | async=async) 831 | bundle.add(port_name, out) 832 | else: 833 | bundle.add(port_name, AddInput(prefix + port_name, port_type)) 834 | else: 835 | port = AddPort(prefix + port_name, port_type, port_role, 836 | posedge=posedge, negedge=negedge, 837 | nodefault=nodefault, async=async) 838 | bundle.add(port_name, port) 839 | 840 | type(addport, role) 841 | return bundle 842 | 843 | 844 | def AddReg(name, type=1, posedge=None, negedge=None, nodefault=False, 845 | async=None, initial=None): 846 | names = name.split() 847 | if len(names) > 1: 848 | return [AddReg(n, type, posedge, negedge, nodefault, async) for n in names] 849 | assert len(names) == 1 850 | name = names[0] 851 | 852 | if not isinstance(type, int): 853 | return AddPort(name, type, "register", posedge=posedge, negedge=negedge, 854 | nodefault=nodefault, async=async) 855 | 856 | assert ChipyCurrentContext is not None 857 | module = ChipyCurrentContext.module 858 | 859 | signal = ChipySignal(module, name) 860 | signal.width = abs(type) 861 | signal.signed = type < 0 862 | signal.register = True 863 | signal.vlog_lvalue = "__next__" + name 864 | signal.set_materialize() 865 | 866 | if posedge is not None or negedge is not None: 867 | AddFF(signal, posedge=posedge, negedge=negedge, nodefault=nodefault, 868 | initial=initial) 869 | 870 | if async: 871 | AddAsync(signal) 872 | 873 | return signal 874 | 875 | 876 | def AddMemory(name, type, depth, posedge=None, negedge=None): 877 | names = name.split() 878 | if len(names) > 1: 879 | return [AddMemory(n, type, depth, posedge, negedge) for n in names] 880 | assert len(names) == 1 881 | name = names[0] 882 | 883 | assert ChipyCurrentContext is not None 884 | module = ChipyCurrentContext.module 885 | 886 | if isinstance(type, int): 887 | return ChipyMemory(module, abs(type), depth, name, posedge=posedge, 888 | negedge=negedge, signed=(type < 0)) 889 | 890 | bundle = Bundle() 891 | prefix = (name + "__") if name != "" else "" 892 | 893 | def addport(port_name, port_type, port_role=None, output=False): 894 | bundle.add(port_name, AddMemory(prefix + port_name, port_type, depth, 895 | posedge=posedge, negedge=negedge)) 896 | 897 | type(addport, "memory") 898 | return bundle 899 | 900 | 901 | def AddFF(signal, posedge=None, negedge=None, nodefault=False, initial=None): 902 | if isinstance(signal, ChipyBundle): 903 | for member in signal.members.values(): 904 | AddFF(member, posedge=posedge, negedge=negedge, nodefault=nodefault, 905 | initial=initial) 906 | return 907 | 908 | assert signal.register and not signal.regaction 909 | num_actions = 0 910 | 911 | snippet = ChipySnippet() 912 | if nodefault: 913 | line = snippet.indent_str + "%s = %d'bx; // %s" \ 914 | % (signal.vlog_lvalue, signal.width, ChipyCodeLoc()) 915 | else: 916 | line = snippet.indent_str + "%s = %s; // %s" \ 917 | % (signal.vlog_lvalue, signal.name, ChipyCodeLoc()) 918 | snippet.text_lines.append(line) 919 | snippet.lvalue_signals[signal.name] = signal 920 | signal.module.init_snippets.append(snippet) 921 | 922 | if initial is not None: 923 | snippet = ChipySnippet() 924 | line = snippet.indent_str + "%s = %d; // %s" \ 925 | % (signal.name, initial, ChipyCodeLoc()) 926 | snippet.text_lines.append(line) 927 | snippet.lvalue_signals[signal.name] = signal 928 | signal.module.initial_snippets.append(snippet) 929 | 930 | if posedge is not None: 931 | raction = " always%s @(posedge %s) %s <= %s; // %s" \ 932 | % ("_ff" if ChipySystemVerilog else "", 933 | posedge.name, signal.name, signal.vlog_lvalue, 934 | ChipyCodeLoc()) 935 | signal.module.regactions.append(raction) 936 | signal.vlog_reg = True 937 | num_actions += 1 938 | 939 | if negedge is not None: 940 | raction = " always%s @(negedge %s) %s <= %s; // %s" \ 941 | % ("_ff" if ChipySystemVerilog else "", 942 | posedge.name, signal.name, signal.vlog_lvalue, 943 | ChipyCodeLoc()) 944 | signal.module.regactions.append(raction) 945 | signal.vlog_reg = True 946 | num_actions += 1 947 | 948 | assert num_actions == 1 949 | signal.regaction = True 950 | 951 | 952 | def AddAsync(signal): 953 | if isinstance(signal, ChipyBundle): 954 | for member in signal.members.values(): 955 | AddAsync(member) 956 | return 957 | 958 | assert signal.register and not signal.regaction 959 | 960 | snippet = ChipySnippet() 961 | line = snippet.indent_str + "%s = %d'bx; // %s" \ 962 | % (signal.vlog_lvalue, signal.width, ChipyCodeLoc()) 963 | snippet.text_lines.append(line) 964 | snippet.lvalue_signals[signal.name] = signal 965 | signal.module.init_snippets.append(snippet) 966 | 967 | raction = " assign %s = %s; // %s" \ 968 | % (signal.name, signal.vlog_lvalue, ChipyCodeLoc()) 969 | signal.module.regactions.append(raction) 970 | signal.regaction = True 971 | 972 | 973 | def AddInst(name, type): 974 | names = name.split() 975 | if len(names) > 1: 976 | return [AddInst(n, type) for n in names] 977 | assert len(names) == 1 978 | name = names[0] 979 | 980 | assert ChipyCurrentContext is not None 981 | module = ChipyCurrentContext.module 982 | 983 | bundle = AddPort(name, type.intf(), "parent") 984 | for signal in bundle.values(): 985 | signal.inport = False 986 | signal.outport = False 987 | 988 | module.instances.append((name, type.name, bundle, ChipyCodeLoc())) 989 | return bundle 990 | 991 | 992 | def Cond(cond, if_val, else_val): 993 | module = ChipySameModule([cond.module, if_val.module, else_val.module]) 994 | 995 | signal = ChipySignal(module) 996 | signal.signed = if_val.signed and else_val.signed 997 | signal.width = max(if_val.width, else_val.width) 998 | signal.vlog_rvalue = "%s ? %s : %s" % (cond.name, if_val.name, else_val.name) 999 | signal.deps[cond.name] = cond 1000 | signal.deps[if_val.name] = if_val 1001 | signal.deps[else_val.name] = else_val 1002 | return signal 1003 | 1004 | 1005 | def Concat(sigs): 1006 | module = None 1007 | width = 0 1008 | rvalues = list() 1009 | lvalues = list() 1010 | deps = dict() 1011 | 1012 | if ChipyCurrentContext is not None: 1013 | module = ChipyCurrentContext.module 1014 | 1015 | for sig in sigs: 1016 | sig = Sig(sig) 1017 | 1018 | if module is None: 1019 | module = sig.module 1020 | elif sig.module is not None: 1021 | assert module is sig.module 1022 | 1023 | if sig.vlog_lvalue is None: 1024 | lvalues = None 1025 | 1026 | if not lvalues is None: 1027 | lvalues.append(sig.vlog_lvalue) 1028 | 1029 | width += sig.width 1030 | rvalues.append(sig.name) 1031 | deps[sig.name] = sig 1032 | 1033 | assert module is not None 1034 | 1035 | signal = ChipySignal(module) 1036 | signal.width = width 1037 | signal.vlog_rvalue = "{%s}" % ",".join(rvalues) 1038 | if lvalues is not None: 1039 | signal.vlog_lvalue = "{%s}" % ",".join(lvalues) 1040 | signal.deps.update(deps) 1041 | 1042 | return signal 1043 | 1044 | 1045 | def Repeat(num, sig): 1046 | sig = Sig(sig) 1047 | 1048 | if ChipyCurrentContext is not None: 1049 | module = ChipyCurrentContext.module 1050 | 1051 | signal = ChipySignal(module) 1052 | signal.width = num * sig.width 1053 | signal.vlog_rvalue = "{%d{%s}}" % (num, sig.name) 1054 | signal.deps[sig.name] = sig 1055 | 1056 | return signal 1057 | 1058 | 1059 | def Connect(sigs): 1060 | if len(sigs) < 2: 1061 | return 1062 | 1063 | if isinstance(sigs[0], ChipyBundle): 1064 | for sig in sigs: 1065 | assert isinstance(sig, ChipyBundle) 1066 | for member in sigs[0].keys(): 1067 | newsigs = list() 1068 | for sig in sigs: 1069 | newsigs.append(sig.get(member)) 1070 | Connect(newsigs) 1071 | return 1072 | 1073 | source_sig = None 1074 | sink_sigs = list() 1075 | 1076 | for sig in sigs: 1077 | if not sig.register or sig.regaction or sig.gotassign: 1078 | assert source_sig is None 1079 | source_sig = sig 1080 | else: 1081 | sink_sigs.append(sig) 1082 | 1083 | assert source_sig is not None 1084 | 1085 | assert ChipyCurrentContext is not None 1086 | module = ChipyCurrentContext.module 1087 | 1088 | for sig in sink_sigs: 1089 | raction = " assign %s = %s; // %s" \ 1090 | % (sig.name, source_sig.name, ChipyCodeLoc()) 1091 | module.regactions.append(raction) 1092 | sig.portalias = source_sig.name 1093 | sig.register = False 1094 | sig.regaction = False 1095 | sig.gotassign = False 1096 | sig.vlog_reg = False 1097 | 1098 | 1099 | def Assign(lhs, rhs): 1100 | if isinstance(lhs, ChipyBundle): 1101 | assert isinstance(rhs, ChipyBundle) 1102 | for member in lhs.members.keys(): 1103 | Assign(lhs.get(member), rhs.get(member)) 1104 | return 1105 | 1106 | lhs = Sig(lhs) 1107 | rhs = Sig(rhs) 1108 | 1109 | rhs.set_materialize() 1110 | 1111 | if lhs.memory is not None: 1112 | module = lhs.module 1113 | wen = ChipySignal(module) 1114 | wen.vlog_reg = True 1115 | wen.gotassign = True 1116 | wen.set_materialize() 1117 | 1118 | snippet = ChipySnippet() 1119 | snippet.text_lines.append(snippet.indent_str + "%s = 1'b0; // %s" 1120 | % (wen.name, ChipyCodeLoc())) 1121 | snippet.lvalue_signals[wen.name] = wen 1122 | module.init_snippets.append(snippet) 1123 | 1124 | ChipyContext() 1125 | ChipyCurrentContext.add_line("%s = 1'b1; // %s" 1126 | % (wen.name, ChipyCodeLoc()), wen.get_deps()) 1127 | ChipyCurrentContext.popctx() 1128 | 1129 | lhs.memory.regactions.append("if (%s) %s <= %s; // %s" 1130 | % (wen.name, lhs.vlog_rvalue, rhs.name, 1131 | ChipyCodeLoc())) 1132 | 1133 | return 1134 | 1135 | assert lhs.vlog_lvalue is not None 1136 | ChipyContext() 1137 | 1138 | lhs_deps = lhs.get_deps() 1139 | for lhs_dep in lhs_deps.values(): 1140 | lhs_dep.gotassign = True 1141 | 1142 | ChipyCurrentContext.add_line("%s = %s; // %s" % (lhs.vlog_lvalue, rhs.name, 1143 | ChipyCodeLoc()), lhs_deps) 1144 | 1145 | ChipyCurrentContext.popctx() 1146 | 1147 | 1148 | def Sig(arg, width=None): 1149 | if isinstance(arg, ChipySignal,): 1150 | if width is not None: 1151 | module = ChipySameModule([arg.module]) 1152 | signal = ChipySignal(module) 1153 | signal.signed = width < 0 1154 | signal.width = abs(width) 1155 | signal.vlog_rvalue = arg.name 1156 | signal.deps[arg.name] = arg 1157 | return signal 1158 | return arg 1159 | 1160 | if isinstance(arg, (tuple, list)): 1161 | assert width is None 1162 | return Concat(arg) 1163 | 1164 | if isinstance(arg, str): 1165 | assert width is None 1166 | assert arg in ChipyCurrentContext.module.signals 1167 | return ChipyCurrentContext.module.signals[arg] 1168 | 1169 | if isinstance(arg, int): 1170 | if width is None: 1171 | width = -32 1172 | var = "%s'%sd%d" % (abs(width), "s" if width < 0 else "", arg) 1173 | signal = ChipySignal(None, var, True) 1174 | signal.signed = width < 0 1175 | signal.width = abs(width) 1176 | return signal 1177 | 1178 | assert 0 1179 | 1180 | 1181 | class If: 1182 | def __init__(self, cond): 1183 | self.cond = cond 1184 | 1185 | def __enter__(self): 1186 | global ChipyElseContext 1187 | ChipyElseContext = None 1188 | 1189 | ChipyContext() 1190 | self.cond.set_materialize() 1191 | ChipyCurrentContext.add_line("if (%s) begin // %s" 1192 | % (self.cond.name, ChipyCodeLoc())) 1193 | ChipyCurrentContext.add_indent() 1194 | 1195 | def __exit__(self, type, value, traceback): 1196 | global ChipyElseContext 1197 | ChipyElseContext = ChipyCurrentContext 1198 | 1199 | ChipyCurrentContext.remove_indent() 1200 | ChipyCurrentContext.add_line("end") 1201 | ChipyCurrentContext.popctx() 1202 | 1203 | 1204 | class ElseIf: 1205 | def __init__(self, cond): 1206 | self.cond = Sig(cond) 1207 | 1208 | def __enter__(self): 1209 | global ChipyElseContext 1210 | ChipyElseContext = None 1211 | 1212 | ChipyElseContext.pushctx() 1213 | self.cond.set_materialize() 1214 | ChipyCurrentContext.add_line("else if (%s) begin // %s" 1215 | % (self.cond.name, ChipyCodeLoc())) 1216 | ChipyCurrentContext.add_indent() 1217 | 1218 | def __exit__(self, type, value, traceback): 1219 | global ChipyElseContext 1220 | ChipyElseContext = ChipyCurrentContext 1221 | 1222 | ChipyCurrentContext.remove_indent() 1223 | ChipyCurrentContext.add_line("end") 1224 | ChipyCurrentContext.popctx() 1225 | 1226 | 1227 | class Else: 1228 | def __init__(self): 1229 | pass 1230 | 1231 | def __enter__(self): 1232 | assert ChipyElseContext is not None 1233 | 1234 | ChipyElseContext.pushctx() 1235 | ChipyCurrentContext.add_line("else begin // %s" % ChipyCodeLoc()) 1236 | ChipyCurrentContext.add_indent() 1237 | 1238 | def __exit__(self, type, value, traceback): 1239 | global ChipyElseContext 1240 | ChipyElseContext = ChipyCurrentContext 1241 | 1242 | ChipyCurrentContext.remove_indent() 1243 | ChipyCurrentContext.add_line("end") 1244 | ChipyCurrentContext.popctx() 1245 | 1246 | Else = Else() 1247 | 1248 | 1249 | class Switch: 1250 | def __init__(self, expr, parallel=False, full=False): 1251 | self.expr = Sig(expr) 1252 | self.parallel = parallel 1253 | self.full = full 1254 | 1255 | def __enter__(self): 1256 | global ChipyElseContext 1257 | ChipyElseContext = None 1258 | 1259 | ChipyContext() 1260 | self.expr.set_materialize() 1261 | if self.parallel: 1262 | ChipyCurrentContext.add_line("(* parallel_case *)") 1263 | if self.full: 1264 | ChipyCurrentContext.add_line("(* full_case *)") 1265 | ChipyCurrentContext.add_line("case (%s) // %s" 1266 | % (self.expr.name, ChipyCodeLoc())) 1267 | ChipyCurrentContext.add_indent() 1268 | 1269 | def __exit__(self, type, value, traceback): 1270 | global ChipyElseContext 1271 | ChipyElseContext = None 1272 | 1273 | ChipyCurrentContext.remove_indent() 1274 | ChipyCurrentContext.add_line("endcase") 1275 | ChipyCurrentContext.popctx() 1276 | 1277 | 1278 | class Case: 1279 | def __init__(self, expr): 1280 | self.expr = Sig(expr) 1281 | 1282 | def __enter__(self): 1283 | global ChipyElseContext 1284 | ChipyElseContext = None 1285 | 1286 | ChipyContext() 1287 | self.expr.set_materialize() 1288 | ChipyCurrentContext.add_line("%s: begin // %s" 1289 | % (self.expr.name, ChipyCodeLoc())) 1290 | ChipyCurrentContext.add_indent() 1291 | 1292 | def __exit__(self, type, value, traceback): 1293 | global ChipyElseContext 1294 | ChipyElseContext = None 1295 | 1296 | ChipyCurrentContext.remove_indent() 1297 | ChipyCurrentContext.add_line("end") 1298 | ChipyCurrentContext.popctx() 1299 | 1300 | 1301 | class Default: 1302 | def __init__(self): 1303 | pass 1304 | 1305 | def __enter__(self): 1306 | global ChipyElseContext 1307 | ChipyElseContext = None 1308 | 1309 | ChipyContext() 1310 | ChipyCurrentContext.add_line("default: begin // %s" % ChipyCodeLoc()) 1311 | ChipyCurrentContext.add_indent() 1312 | 1313 | def __exit__(self, type, value, traceback): 1314 | global ChipyElseContext 1315 | ChipyElseContext = None 1316 | 1317 | ChipyCurrentContext.remove_indent() 1318 | ChipyCurrentContext.add_line("end") 1319 | ChipyCurrentContext.popctx() 1320 | 1321 | Default = Default() 1322 | 1323 | 1324 | def Stream(data_type, last=False, destbits=0): 1325 | def callback(addport, role): 1326 | addport("valid", 1, output=(role == "source")) 1327 | addport("ready", 1, output=(role == "sink")) 1328 | addport("data", data_type, output=(role == "source")) 1329 | if last: 1330 | addport("last", 1, output=(role == "source")) 1331 | if destbits != 0: 1332 | addport("dest", destbits, output=(role == "source")) 1333 | return callback 1334 | 1335 | 1336 | def WriteVerilog(f): 1337 | print("// Generated using Chipy (Constructing Hardware In PYthon)", file=f) 1338 | for modname, module in ChipyModulesDict.items(): 1339 | module.write_verilog(f) 1340 | --------------------------------------------------------------------------------