diff --git a/examples/GW1NSR-4C/.gitignore b/examples/GW1NSR-4C/.gitignore new file mode 100644 index 0000000..d28717d --- /dev/null +++ b/examples/GW1NSR-4C/.gitignore @@ -0,0 +1,2 @@ +OUT +BUILD \ No newline at end of file diff --git a/examples/GW1NSR-4C/CON/io.cst b/examples/GW1NSR-4C/CON/io.cst new file mode 100644 index 0000000..185bd53 --- /dev/null +++ b/examples/GW1NSR-4C/CON/io.cst @@ -0,0 +1,8 @@ +IO_LOC "led_o" 10; +IO_PORT "led_o" PULL_MODE=NONE DRIVE=8; + +IO_LOC "reset_n_i" 15; +IO_PORT "reset_n_i" PULL_MODE=UP; + +IO_LOC "clk_i" 45; +IO_PORT "clk_i" PULL_MODE=UP; \ No newline at end of file diff --git a/examples/GW1NSR-4C/CON/io.sdc b/examples/GW1NSR-4C/CON/io.sdc new file mode 100644 index 0000000..198a56c --- /dev/null +++ b/examples/GW1NSR-4C/CON/io.sdc @@ -0,0 +1 @@ +create_clock -name CLK_IN -period 37.037 -waveform {0 18.52} [get_ports {clk_i}] \ No newline at end of file diff --git a/examples/GW1NSR-4C/RTL/toplevel.v b/examples/GW1NSR-4C/RTL/toplevel.v new file mode 100644 index 0000000..077bf5e --- /dev/null +++ b/examples/GW1NSR-4C/RTL/toplevel.v @@ -0,0 +1,23 @@ +module led_blink +( + input clk_i, + input reset_n_i, + output led_o +); + + reg [23:0] counter; + reg polarity; + + always@(posedge clk_i) begin + if (!reset_n_i) + counter <= 24'h00000; + else + counter <= counter + 1'b1; + + if (counter == 24'hFFFFF) + polarity <= ~polarity; + end + + assign led_o = polarity; + +endmodule \ No newline at end of file diff --git a/examples/GW1NSR-4C/project.cfg b/examples/GW1NSR-4C/project.cfg new file mode 100644 index 0000000..3ce2634 --- /dev/null +++ b/examples/GW1NSR-4C/project.cfg @@ -0,0 +1,28 @@ +[project] +name = gowin_project +version = 0.1 +out_dir = OUT +build_dir = BUILD + +[server] +hostname = localhost +port = 2020 +privkey = /home/joppe/.ssh/id_rsa +pubkey = /home/joppe/.ssh/id_rsa.pub + +# ###################################### +# Basic synthesis +[target.synth] +toolchain = gowin + +# Toolchain settings +family = GW1NSR-4C +device = GW1NSR-LV4CQN48PC6/I5 +toplevel = led_blink + +# Fileset +# files_vhdl = +files_verilog = RTL/toplevel.v +files_con = CON/io.cst + CON/io.sdc +# ###################################### \ No newline at end of file diff --git a/remotesyn/toolchains/gowin.py b/remotesyn/toolchains/gowin.py new file mode 100644 index 0000000..05b3030 --- /dev/null +++ b/remotesyn/toolchains/gowin.py @@ -0,0 +1,135 @@ +import shutil +import os +import time +import subprocess +import html2text + +def do(config, target, log, subprocesses, prefix='.'): + shutil.rmtree(config.get('project', 'build_dir', fallback='build'), True) + shutil.rmtree(config.get('project', 'out_dir', fallback='out'), True) + + log(" - parsing options") + family = config.get(f'target.{target}', 'family', fallback='') + device = config.get(f'target.{target}', 'device', fallback='') + toplevel = config.get(f'target.{target}', 'toplevel', fallback='toplevel') + files_vhdl = config.get(f'target.{target}', 'files_vhdl', fallback='').split() + files_verilog = config.get(f'target.{target}', 'files_verilog', fallback='').split() + files_con= config.get(f'target.{target}', 'files_con', fallback='').split() + build_dir = config.get(f'project', 'build_dir', fallback='build') + out_dir = config.get(f'project', 'out_dir', fallback='out') + + prefix = f'{os.getcwd()}/{prefix}' + build_dir = f'{prefix}/{build_dir}' + out_dir = f'{prefix}/{out_dir}/{target}' + + log(" - creating output directories") + os.makedirs(build_dir, exist_ok=True) + os.makedirs(out_dir, exist_ok=True) + + log(" - writing scripts") + with open(f'{build_dir}/synth.tcl', 'w') as f: + f.write(f'set_device {device} -name {family}\n') + + for s in files_vhdl: + if s=='': + continue + f.write(f'add_file {prefix}/{s}\n') + for s in files_verilog: + if s=='': + continue + f.write(f'add_file {prefix}/{s}\n') + for s in files_con: + if s=='': + continue + f.write(f'add_file {prefix}/{s}\n') + + f.write('set_option -synthesis_tool gowinsynthesis\n') + f.write(f'set_option -output_base_name {toplevel}\n') + f.write(f'set_option -top_module {toplevel}\n') + f.write('run syn\n') + + with open(f'{build_dir}/pnr.tcl', 'w') as f: + f.write(f'set_device {device} -name {family}\n') + f.write('set_option -synthesis_tool gowinsynthesis\n') + f.write(f'set_option -output_base_name {toplevel}\n') + f.write(f'set_option -top_module {toplevel}\n') + for s in files_con: + if s=='': + continue + f.write(f'add_file {prefix}/{s}\n') + f.write(f'add_file {build_dir}/impl/gwsynthesis/{toplevel}.vg\n') + + f.write('run pnr\n') + + log(' - run syntesis') + p = subprocess.Popen(f"gw_sh synth.tcl", + shell = True, + cwd = build_dir, + stdin = subprocess.DEVNULL, + # stdout = subprocess.DEVNULL, + # stderr = subprocess.DEVNULL, + ) + + subprocesses.append(p) + while p.poll() is None: + time.sleep(1) + res = p.returncode + + shutil.copy(f'{build_dir}/impl/gwsynthesis/{toplevel}.log', f'{out_dir}/synth.log') + if res!=0: + log("ERROR: synthesis failed with:", res) + return res + shutil.copy(f'{build_dir}/impl/gwsynthesis/{toplevel}.vg', f'{out_dir}/synth_netlist.vg') + with open(f'{build_dir}/impl/gwsynthesis/{toplevel}_syn.rpt.html', 'r') as fin, open(f'{out_dir}/synth_rpt.md', 'w') as fout: + text_maker = html2text.HTML2Text() + text_maker.bypass_tables = False + text_maker.ignore_links = True + text_maker.body_width = 500 + fout.write(text_maker.handle(fin.read())) + + log(' - run PnR') + p = subprocess.Popen(f"gw_sh pnr.tcl", + shell = True, + cwd = build_dir, + stdin = subprocess.DEVNULL, + # stdout = subprocess.DEVNULL, + # stderr = subprocess.DEVNULL, + ) + + subprocesses.append(p) + while p.poll() is None: + time.sleep(1) + res = p.returncode + + shutil.copy(f'{build_dir}/impl/pnr/{toplevel}.log', f'{out_dir}/pnr.log') + if res!=0: + log("ERROR: pnr failed with:", res) + return res + shutil.copy(f'{build_dir}/impl/pnr/{toplevel}.fs', f'{out_dir}/{toplevel}.fs') + shutil.copy(f'{build_dir}/impl/pnr/{toplevel}.bin', f'{out_dir}/{toplevel}.bin') + with open(f'{build_dir}/impl/pnr/{toplevel}.rpt.html', 'r') as fin, open(f'{out_dir}/rpt.md', 'w') as fout: + text_maker = html2text.HTML2Text() + text_maker.bypass_tables = False + text_maker.ignore_links = True + text_maker.body_width = 500 + fout.write(text_maker.handle(fin.read())) + with open(f'{build_dir}/impl/pnr/{toplevel}.power.html', 'r') as fin, open(f'{out_dir}/power.md', 'w') as fout: + text_maker = html2text.HTML2Text() + text_maker.bypass_tables = False + text_maker.ignore_links = True + text_maker.body_width = 500 + fout.write(text_maker.handle(fin.read())) + with open(f'{build_dir}/impl/pnr/{toplevel}.pin.html', 'r') as fin, open(f'{out_dir}/pin.md', 'w') as fout: + text_maker = html2text.HTML2Text() + text_maker.bypass_tables = False + text_maker.ignore_links = True + text_maker.body_width = 500 + fout.write(text_maker.handle(fin.read())) + with open(f'{build_dir}/impl/pnr/{toplevel}_tr_content.html', 'r') as fin, open(f'{out_dir}/timing.md', 'w') as fout: + text_maker = html2text.HTML2Text() + text_maker.bypass_tables = False + text_maker.ignore_links = True + text_maker.body_width = 500 + fout.write(text_maker.handle(fin.read())) + + return res diff --git a/setup.py b/setup.py index 79b77a8..449b9d6 100644 --- a/setup.py +++ b/setup.py @@ -20,6 +20,6 @@ setup( ], packages=['remotesyn'], licence='BSD Licence', - install_requires=['paramiko'], + install_requires=['paramiko', 'html2text'], scripts=['scripts/rbuild', 'scripts/rmbuild', 'scripts/rmserver'] ) \ No newline at end of file