Skip to content

Commit 7240d45

Browse files
authored
Merge pull request #56 from estodi/feature_thread_print_fstrings
Add f-strings support in `print()` within threads
2 parents 3dc019d + aed93d4 commit 7240d45

File tree

4 files changed

+292
-0
lines changed

4 files changed

+292
-0
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
TARGET=$(shell ls *.py | grep -v test | grep -v parsetab.py)
2+
ARGS=
3+
4+
PYTHON=python3
5+
#PYTHON=python
6+
#OPT=-m pdb
7+
#OPT=-m cProfile -s time
8+
#OPT=-m cProfile -o profile.rslt
9+
10+
.PHONY: all
11+
all: test
12+
13+
.PHONY: run
14+
run:
15+
$(PYTHON) $(OPT) $(TARGET) $(ARGS)
16+
17+
.PHONY: test
18+
test:
19+
$(PYTHON) -m pytest -vv
20+
21+
.PHONY: check
22+
check:
23+
$(PYTHON) $(OPT) $(TARGET) $(ARGS) > tmp.v
24+
iverilog -tnull -Wall tmp.v
25+
rm -f tmp.v
26+
27+
.PHONY: clean
28+
clean:
29+
rm -rf *.pyc __pycache__ parsetab.py .cache *.out *.png *.dot tmp.v *.vcd
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
from __future__ import absolute_import
2+
from __future__ import print_function
3+
import veriloggen
4+
import thread_print_fstrings
5+
6+
expected_verilog = """
7+
module test
8+
(
9+
10+
);
11+
12+
reg CLK;
13+
reg RST;
14+
wire [8-1:0] LED;
15+
16+
blinkled
17+
uut
18+
(
19+
.CLK(CLK),
20+
.RST(RST),
21+
.LED(LED)
22+
);
23+
24+
25+
initial begin
26+
CLK = 0;
27+
forever begin
28+
#5 CLK = !CLK;
29+
end
30+
end
31+
32+
33+
initial begin
34+
RST = 0;
35+
#100;
36+
RST = 1;
37+
#100;
38+
RST = 0;
39+
#50000;
40+
$finish;
41+
end
42+
43+
44+
endmodule
45+
46+
47+
48+
module blinkled
49+
(
50+
input CLK,
51+
input RST,
52+
output reg [8-1:0] LED
53+
);
54+
55+
reg [10-1:0] CNT;
56+
reg [32-1:0] th_blink;
57+
localparam th_blink_init = 0;
58+
localparam th_blink_1 = 1;
59+
localparam th_blink_2 = 2;
60+
localparam th_blink_3 = 3;
61+
localparam th_blink_4 = 4;
62+
localparam th_blink_5 = 5;
63+
localparam th_blink_6 = 6;
64+
localparam th_blink_7 = 7;
65+
localparam th_blink_8 = 8;
66+
localparam th_blink_9 = 9;
67+
localparam th_blink_10 = 10;
68+
localparam th_blink_11 = 11;
69+
70+
always @(posedge CLK) begin
71+
if(RST) begin
72+
th_blink <= th_blink_init;
73+
LED <= 0;
74+
CNT <= 0;
75+
end else begin
76+
case(th_blink)
77+
th_blink_init: begin
78+
th_blink <= th_blink_1;
79+
end
80+
th_blink_1: begin
81+
$display("Hello, world!");
82+
th_blink <= th_blink_2;
83+
end
84+
th_blink_2: begin
85+
if(1) begin
86+
th_blink <= th_blink_3;
87+
end else begin
88+
th_blink <= th_blink_11;
89+
end
90+
end
91+
th_blink_3: begin
92+
LED <= LED + 1;
93+
th_blink <= th_blink_4;
94+
end
95+
th_blink_4: begin
96+
CNT <= CNT + 3;
97+
th_blink <= th_blink_5;
98+
end
99+
th_blink_5: begin
100+
if(LED % 70 == 0) begin
101+
th_blink <= th_blink_6;
102+
end else begin
103+
th_blink <= th_blink_10;
104+
end
105+
end
106+
th_blink_6: begin
107+
$display("");
108+
th_blink <= th_blink_7;
109+
end
110+
th_blink_7: begin
111+
$display("led = %0d (%b)", LED, LED);
112+
th_blink <= th_blink_8;
113+
end
114+
th_blink_8: begin
115+
$display("cnt = %0d (%b)", CNT, CNT);
116+
th_blink <= th_blink_9;
117+
end
118+
th_blink_9: begin
119+
$display("cnt + led = %d", (CNT + LED));
120+
th_blink <= th_blink_10;
121+
end
122+
th_blink_10: begin
123+
th_blink <= th_blink_2;
124+
end
125+
endcase
126+
end
127+
end
128+
129+
130+
endmodule
131+
"""
132+
133+
134+
def test():
135+
veriloggen.reset()
136+
test_module = thread_print_fstrings.mkTest()
137+
code = test_module.to_verilog()
138+
139+
from pyverilog.vparser.parser import VerilogParser
140+
from pyverilog.ast_code_generator.codegen import ASTCodeGenerator
141+
parser = VerilogParser()
142+
expected_ast = parser.parse(expected_verilog)
143+
codegen = ASTCodeGenerator()
144+
expected_code = codegen.visit(expected_ast)
145+
146+
assert(expected_code == code)
147+
148+
test()
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
from __future__ import absolute_import
2+
from __future__ import print_function
3+
import sys
4+
import os
5+
6+
# the next line can be removed after installation
7+
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(
8+
os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))))
9+
10+
from veriloggen import *
11+
import veriloggen.thread as vthread
12+
13+
14+
def mkLed():
15+
m = Module('blinkled')
16+
clk = m.Input('CLK')
17+
rst = m.Input('RST')
18+
led = m.OutputReg('LED', 8, initval=0)
19+
cnt = m.Reg('CNT', 10, initval=0)
20+
21+
def blink():
22+
print('Hello, world!')
23+
while True:
24+
led.value += 1
25+
cnt.value += 3
26+
if led.value % 70 == 0:
27+
print()
28+
print('led = %0d (%b)' % (led, led))
29+
print(f'{cnt = :0d} ({cnt:b})')
30+
print(f'cnt + led = {cnt + led}')
31+
32+
th = vthread.Thread(m, 'th_blink', clk, rst, blink)
33+
fsm = th.start()
34+
35+
return m
36+
37+
38+
def mkTest():
39+
m = Module('test')
40+
41+
# target instance
42+
led = mkLed()
43+
44+
# copy paras and ports
45+
params = m.copy_params(led)
46+
ports = m.copy_sim_ports(led)
47+
48+
clk = ports['CLK']
49+
rst = ports['RST']
50+
51+
uut = m.Instance(led, 'uut',
52+
params=m.connect_params(led),
53+
ports=m.connect_ports(led))
54+
55+
# vcd_name = os.path.splitext(os.path.basename(__file__))[0] + '.vcd'
56+
# simulation.setup_waveform(m, uut, dumpfile=vcd_name)
57+
simulation.setup_clock(m, clk, hperiod=5)
58+
init = simulation.setup_reset(m, rst, m.make_reset(), period=100)
59+
60+
init.add(
61+
Delay(50000),
62+
Systask('finish'),
63+
)
64+
65+
return m
66+
67+
68+
if __name__ == '__main__':
69+
test = mkTest()
70+
verilog = test.to_verilog('tmp.v')
71+
print(verilog)
72+
73+
sim = simulation.Simulator(test)
74+
rslt = sim.run()
75+
print(rslt)

veriloggen/thread/compiler.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,25 @@ def _call_Name_print(self, node):
471471
formatstring_list.append(form)
472472
formatstring_list.append(" ")
473473

474+
elif (sys.version_info >= (3, 6) and
475+
isinstance(arg, ast.JoinedStr)):
476+
# Formatted String Literals (f-strings) in print statement
477+
values, form = self._print_f_strings(arg)
478+
479+
for value in values:
480+
if isinstance(value, fxd._FixedBase):
481+
if value.point >= 0:
482+
argvalues.append(vtypes.Div(vtypes.SystemTask('itor', value),
483+
1.0 * (2 ** value.point)))
484+
else:
485+
argvalues.append(vtypes.Times(value, 2 ** -value.point))
486+
487+
else:
488+
argvalues.append(value)
489+
490+
formatstring_list.append(form)
491+
formatstring_list.append(" ")
492+
474493
elif isinstance(arg, ast.Tuple):
475494
for e in arg.elts:
476495
value = self.visit(e)
@@ -535,6 +554,27 @@ def _print_binop_mod(self, arg):
535554
form = arg.left.s
536555
return values, form
537556

557+
def _print_f_strings(self, arg):
558+
values = []
559+
form = ''
560+
for val in arg.values:
561+
if isinstance(val, ast.Constant):
562+
form += val.value
563+
elif isinstance(val, ast.FormattedValue):
564+
values.append(self.visit(val.value))
565+
if val.format_spec:
566+
# no guarantee if a simulator accepts it or not
567+
if len(val.format_spec.values) == 1:
568+
# e.g. f"{reg0:x} {reg1:0b}"
569+
form += '%' + val.format_spec.values[0].value
570+
else:
571+
# do not allow nested f-strings
572+
raise SyntaxError('Illegal format_spec of f-strings')
573+
else:
574+
# interpret as integer by default
575+
form += '%d'
576+
return values, form
577+
538578
def _call_Name_int(self, node):
539579
if len(node.args) > 1:
540580
raise TypeError(

0 commit comments

Comments
 (0)