diff --git a/.gitignore b/.gitignore index f1fa65a..2fe4512 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ out build -env \ No newline at end of file +env +__pycache* +_impactbatch.log \ No newline at end of file diff --git a/boards/mimas_v1/constraints.ucf b/boards/mimas_v1/constraints.ucf index 534d841..9c79021 100644 --- a/boards/mimas_v1/constraints.ucf +++ b/boards/mimas_v1/constraints.ucf @@ -24,4 +24,29 @@ NET "r2r[1]" IOSTANDARD = LVCMOS33; NET "r2r[2]" IOSTANDARD = LVCMOS33; NET "r2r[3]" IOSTANDARD = LVCMOS33; NET "r2r[4]" IOSTANDARD = LVCMOS33; -NET "r2r[5]" IOSTANDARD = LVCMOS33; \ No newline at end of file +NET "r2r[5]" IOSTANDARD = LVCMOS33; + +NET "LED[0]" LOC = P119; +NET "LED[0]" IOSTANDARD = LVCMOS33; +NET "LED[0]" DRIVE = 8; +NET "LED[1]" LOC = P118; +NET "LED[1]" IOSTANDARD = LVCMOS33; +NET "LED[1]" DRIVE = 8; +NET "LED[2]" LOC = P117; +NET "LED[2]" IOSTANDARD = LVCMOS33; +NET "LED[2]" DRIVE = 8; +NET "LED[3]" LOC = P116; +NET "LED[3]" IOSTANDARD = LVCMOS33; +NET "LED[3]" DRIVE = 8; +NET "LED[4]" LOC = P115; +NET "LED[4]" IOSTANDARD = LVCMOS33; +NET "LED[4]" DRIVE = 8; +NET "LED[5]" LOC = P114; +NET "LED[5]" IOSTANDARD = LVCMOS33; +NET "LED[5]" DRIVE = 8; +NET "LED[6]" LOC = P112; +NET "LED[6]" IOSTANDARD = LVCMOS33; +NET "LED[6]" DRIVE = 8; +NET "LED[7]" LOC = P111; +NET "LED[7]" IOSTANDARD = LVCMOS33; +NET "LED[7]" DRIVE = 8; \ No newline at end of file diff --git a/project.cfg b/project.cfg index bd9ec22..c0965b0 100644 --- a/project.cfg +++ b/project.cfg @@ -4,6 +4,15 @@ version = 0.1 out_dir = out build_dir = build +[target.ip] +toolchain = ISE_IP +ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh +family = spartan6 +device = xc6slx9 +package = tqg144 +speedgrade = -2 +files_def = boards/mimas_v1/ip/clk_gen.xco + [target.synth] toolchain = ISE ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh @@ -56,31 +65,25 @@ files_other = rtl/util/rc_alpha_q15.vh rtl/util/clog2.vh sw/blinky/blinky.hex -[target.ip] -toolchain = ISE_IP +[target.jtag] +toolchain = ISE ise_settings = /opt/Xilinx/14.7/ISE_DS/settings64.sh family = spartan6 device = xc6slx9 package = tqg144 speedgrade = -2 -files_def = boards/mimas_v1/ip/clk_gen.xco +toplevel = top_jtag +xst_opts = -vlgincdir rtl/util +files_other = +files_con = boards/mimas_v1/constraints.ucf +files_verilog = rtl/arch/spartan-6/jtag_if.v + rtl/arch/spartan-6/clk_gen.v + rtl/toplevel/top_jtag.v -[target.sim] +[target.svftest] toolchain = iverilog runtime = all -toplevel = tb_sigmadelta -ivl_opts = -Irtl/util -files_verilog = sim/tb/tb_nco_q15.v - sim/tb/tb_sigmadelta.v - sim/tb/tb_mul_const.v - rtl/core/nco_q15.v - rtl/core/lvds_comparator.v - rtl/core/sigmadelta_rcmodel_q15.v - rtl/core/sigmadelta_input_q15.v - rtl/core/mul_const.v - rtl/core/lpf_iir_q15_k.v - rtl/core/decimate_by_r_q15.v - sim/overrides/sigmadelta_sampler.v - sim/overrides/clk_gen.v -files_other = rtl/util/conv.vh - rtl/util/rc_alpha_q15.vh +toplevel = tb_svf +files_verilog = sim/tb/tb_svf.v + sim/overrides/jtag_if.v +files_other = sim/other/test.svf diff --git a/rtl/arch/spartan-6/jtag_if.v b/rtl/arch/spartan-6/jtag_if.v new file mode 100644 index 0000000..53da708 --- /dev/null +++ b/rtl/arch/spartan-6/jtag_if.v @@ -0,0 +1,34 @@ +`timescale 1ns/1ps + +// ============================================================================= +// JTAG interface +// Spartan-6 BSCAN primitive wrapper (USER1 chain). +// ============================================================================= +module jtag_if #( + parameter chain = 1 +)( + input wire i_tdo, + output wire o_tck, + output wire o_tdi, + output wire o_drck, + output wire o_capture, + output wire o_shift, + output wire o_update, + output wire o_runtest, + output wire o_reset, + output wire o_sel +); + BSCAN_SPARTAN6 #( + .JTAG_CHAIN(chain) + ) bscan_i ( + .CAPTURE(o_capture), + .DRCK(o_drck), + .RESET(o_reset), + .RUNTEST(o_runtest), + .SEL(o_sel), + .SHIFT(o_shift), + .TCK(o_tck), + .TDI(o_tdi), + .UPDATE(o_update) + ); +endmodule diff --git a/rtl/core/jtag_if.v b/rtl/core/jtag_if.v new file mode 100644 index 0000000..d53b31f --- /dev/null +++ b/rtl/core/jtag_if.v @@ -0,0 +1,34 @@ +`timescale 1ns/1ps + +// ============================================================================= +// JTAG interface +// Generic stub model with inactive/tied-off outputs. +// ============================================================================= +module jtag_if #( + parameter chain = 1 +)( + input wire i_tdo, + output wire o_tck, + output wire o_tdi, + output wire o_drck, + output wire o_capture, + output wire o_shift, + output wire o_update, + output wire o_runtest, + output wire o_reset, + output wire o_sel +); + assign o_tck = 1'b0; + assign o_tdi = 1'b0; + assign o_drck = 1'b0; + assign o_capture = 1'b0; + assign o_shift = 1'b0; + assign o_update = 1'b0; + assign o_runtest = 1'b0; + assign o_reset = 1'b0; + assign o_sel = 1'b0; + + // Keep lint tools quiet in generic builds where TDO is unused. + wire _unused_tdo; + assign _unused_tdo = i_tdo; +endmodule diff --git a/rtl/toplevel/top_jtag.v b/rtl/toplevel/top_jtag.v new file mode 100644 index 0000000..9a730e1 --- /dev/null +++ b/rtl/toplevel/top_jtag.v @@ -0,0 +1,131 @@ +module top_jtag( + input wire aclk, + input wire aresetn, + + output wire led_green, + output wire led_red, + output wire [7:0] LED, + + output wire [5:0] r2r +); + // Clocking + wire clk_100; + wire clk_15; + assign clk_100 = aclk; + clk_gen clocking( + .clk_in(clk_100), + .clk_out_15(clk_15) + ); + + localparam integer JTAG_DATA_BITS = 8; + wire [JTAG_DATA_BITS-1:0] jtag_data; + + jtag_write_reg #( + .DATA_BITS(JTAG_DATA_BITS), + .CHAIN(1) + ) jtag_writer ( + .clk_i(clk_15), + .rst_n_i(aresetn), + .data_o(jtag_data) + ); + + assign LED = jtag_data[7:0]; + assign led_green = jtag_data[0]; + assign led_red = jtag_data[7]; + assign r2r = jtag_data[5:0]; + +endmodule + +module jtag_write_reg #( + parameter integer DATA_BITS = 8, + parameter integer CHAIN = 1 +)( + input wire clk_i, + input wire rst_n_i, + output reg [DATA_BITS-1:0] data_o +); + wire jtag_tck; + wire jtag_tdi; + wire jtag_drck; + wire jtag_capture; + wire jtag_shift; + wire jtag_update; + wire jtag_runtest; + wire jtag_reset; + wire jtag_sel; + reg [DATA_BITS-1:0] shift_q; + reg [DATA_BITS-1:0] data_jtag_q; + reg update_toggle_jtag_q; + reg update_toggle_meta_q; + reg update_toggle_sync_q; + reg update_toggle_sync_d_q; + reg [DATA_BITS-1:0] data_meta_q; + reg [DATA_BITS-1:0] data_sync_q; + + jtag_if #( + .chain(CHAIN) + ) jtag ( + .i_tdo(shift_q[0]), + .o_tck(jtag_tck), + .o_tdi(jtag_tdi), + .o_drck(jtag_drck), + .o_capture(jtag_capture), + .o_shift(jtag_shift), + .o_update(jtag_update), + .o_runtest(jtag_runtest), + .o_reset(jtag_reset), + .o_sel(jtag_sel) + ); + + always @(posedge jtag_drck or posedge jtag_reset or negedge rst_n_i) begin + if (!rst_n_i || jtag_reset) begin + shift_q <= {DATA_BITS{1'b0}}; + end else if (jtag_sel && jtag_capture) begin + shift_q <= data_jtag_q; + end else if (jtag_sel && jtag_shift) begin + if (DATA_BITS == 1) begin + shift_q <= jtag_tdi; + end else begin + shift_q <= {jtag_tdi, shift_q[DATA_BITS-1:1]}; + end + end + end + + always @(posedge jtag_update or posedge jtag_reset or negedge rst_n_i) begin + if (!rst_n_i || jtag_reset) begin + data_jtag_q <= {DATA_BITS{1'b0}}; + update_toggle_jtag_q <= 1'b0; + end else if (jtag_sel) begin + data_jtag_q <= shift_q; + update_toggle_jtag_q <= ~update_toggle_jtag_q; + end + end + + // CDC into clk_i domain: toggle synchronize + stable data sampling. + always @(posedge clk_i or negedge rst_n_i) begin + if (!rst_n_i) begin + update_toggle_meta_q <= 1'b0; + update_toggle_sync_q <= 1'b0; + update_toggle_sync_d_q <= 1'b0; + data_meta_q <= {DATA_BITS{1'b0}}; + data_sync_q <= {DATA_BITS{1'b0}}; + data_o <= {DATA_BITS{1'b0}}; + end else begin + update_toggle_meta_q <= update_toggle_jtag_q; + update_toggle_sync_q <= update_toggle_meta_q; + update_toggle_sync_d_q <= update_toggle_sync_q; + data_meta_q <= data_jtag_q; + data_sync_q <= data_meta_q; + + if (update_toggle_sync_q ^ update_toggle_sync_d_q) begin + data_o <= data_sync_q; + end + end + end + + // Not used for this simple write-register, but kept for completeness. + wire _unused_tck; + wire _unused_runtest; + assign _unused_tck = jtag_tck; + assign _unused_runtest = jtag_runtest; +endmodule diff --git a/scripts/jtag_write_user_impact.py b/scripts/jtag_write_user_impact.py new file mode 100755 index 0000000..b206e4b --- /dev/null +++ b/scripts/jtag_write_user_impact.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +"""Write USER JTAG data via iMPACT using SVF + play flow.""" + +from __future__ import annotations + +import argparse +import os +import shutil +import subprocess +import sys +import tempfile + + +DEFAULT_IMPACT = "/opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64/impact" +DEFAULT_SETTINGS = "/opt/Xilinx/14.7/ISE_DS/settings64.sh" + + +def _parse_int(value: str) -> int: + return int(value, 0) + + +def _resolve_impact(binary: str | None) -> str | None: + if binary: + return binary if os.path.isfile(binary) else shutil.which(binary) + if os.path.isfile(DEFAULT_IMPACT): + return DEFAULT_IMPACT + return shutil.which("impact") + + +def _fmt_hex_for_bits(value: int, bits: int) -> str: + masked = value & ((1 << bits) - 1) + nibbles = (bits + 3) // 4 + return f"{masked:0{nibbles}X}" + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Write USER JTAG data to Spartan-6 via iMPACT play (SVF)." + ) + parser.add_argument( + "--input", + required=True, + help="Input file to stream over USER JTAG (written byte-by-byte).", + ) + parser.add_argument("--output", type=str, default=None) + parser.add_argument("--dr-bits", type=int, default=8, help="DR width in bits (default: 8).") + parser.add_argument("--ir-value", type=_parse_int, default=0x02, help="IR opcode (default USER1: 0x02).") + parser.add_argument("--ir-bits", type=int, default=6, help="IR width in bits (default: 6).") + parser.add_argument("--serial", default=None, help="Cable ESN/serial (example: D306180FABCD).") + parser.add_argument("--impact", default=None, help=f"Path/name of impact binary (default: {DEFAULT_IMPACT}).") + parser.add_argument( + "--settings", + default=DEFAULT_SETTINGS, + help=f"Xilinx settings script sourced for this run only (default: {DEFAULT_SETTINGS}).", + ) + parser.add_argument("--dry-run", action="store_true", help="Print generated SVF/CMD and exit.") + args = parser.parse_args() + + if args.dr_bits <= 0 or args.ir_bits <= 0: + print("error: --dr-bits and --ir-bits must be > 0", file=sys.stderr) + return 2 + if args.dr_bits != 8: + print("error: this file-stream mode currently supports only --dr-bits 8", file=sys.stderr) + return 2 + + impact = _resolve_impact(args.impact) + if impact is None: + print("error: impact binary not found", file=sys.stderr) + return 127 + if not os.path.isfile(args.input): + print(f'error: input file not found: "{args.input}"', file=sys.stderr) + return 2 + + ir_hex = _fmt_hex_for_bits(args.ir_value, args.ir_bits) + with open(args.input, "rb") as f: + payload = f.read() + if len(payload) == 0: + print("error: input file is empty", file=sys.stderr) + return 2 + + svf_lines = [ + "! Auto-generated by jtag_write_user_impact.py", + f"! Source file: {args.input} ({len(payload)} bytes)", + "TRST ABSENT;", + "ENDIR IDLE;", + "ENDDR IDLE;", + "STATE RESET;", + "STATE IDLE;", + f"SIR {args.ir_bits} TDI ({ir_hex});", + ] + for byte in payload: + svf_lines.append(f"SDR 8 TDI ({byte:02X});") + svf_lines += [ + "RUNTEST 16 TCK;", + "STATE IDLE;", + "", + ] + svf_text = "\n".join(svf_lines) + + cable_cmd = "setCable -port auto" + if args.serial: + cable_cmd += f" -esn {args.serial}" + + if args.dry_run: + print("### SVF ###") + preview_limit = 64 + if len(payload) <= preview_limit: + print(svf_text, end="") + else: + preview = "\n".join(svf_lines[:8 + preview_limit]) + print(preview) + print(f"... ({len(payload) - preview_limit} more SDR lines omitted)") + print("### iMPACT CMD ###") + print("setMode -bs") + print(cable_cmd) + print("addDevice -p 1 -file ") + print("play") + print("quit") + return 0 + + svf_path = None + cmd_path = None + try: + with tempfile.NamedTemporaryFile("w", suffix=".svf", delete=False) as sf: + sf.write(svf_text) + svf_path = sf.name + if args.output is not None: + with open(args.output, "w") as sf: + sf.write(svf_text) + + cmd_text = "\n".join( + [ + "setMode -bs", + cable_cmd, + f'addDevice -p 1 -file "{svf_path}"', + "play", + "quit", + "", + ] + ) + with tempfile.NamedTemporaryFile("w", suffix=".cmd", delete=False) as cf: + cf.write(cmd_text) + cmd_path = cf.name + + shell_cmd = ( + f'source "{args.settings}" >/dev/null 2>&1 && ' + f'"{impact}" -batch "{cmd_path}"' + ) + proc = subprocess.run(["bash", "-lc", shell_cmd], text=True, capture_output=True) + output = (proc.stdout or "") + (proc.stderr or "") + print(output, end="") + return proc.returncode + finally: + if cmd_path is not None: + try: + os.unlink(cmd_path) + except OSError: + pass + if svf_path is not None: + try: + os.unlink(svf_path) + except OSError: + pass + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/sim/other/test.svf b/sim/other/test.svf new file mode 100644 index 0000000..90808c7 --- /dev/null +++ b/sim/other/test.svf @@ -0,0 +1,25 @@ +TRST ABSENT; +ENDIR IDLE; +ENDDR IDLE; +STATE RESET; +STATE IDLE; +SIR 6 TDI (02); + +SDR 8 TDI (07); +SDR 8 TDI (4C); +SDR 8 TDI (43); +SDR 8 TDI (D1); +SDR 8 TDI (97); +SDR 8 TDI (6B); +SDR 8 TDI (3D); +SDR 8 TDI (E4); +SDR 8 TDI (20); +SDR 8 TDI (3A); +SDR 8 TDI (8E); +SDR 8 TDI (1D); +SDR 8 TDI (9D); +SDR 8 TDI (E4); + +RUNTEST 16 TCK; +STATE RESET; +STATE IDLE; diff --git a/sim/overrides/jtag_if.v b/sim/overrides/jtag_if.v new file mode 100644 index 0000000..443dcfd --- /dev/null +++ b/sim/overrides/jtag_if.v @@ -0,0 +1,196 @@ +`timescale 1ns/1ps + +// ============================================================================= +// JTAG interface (simulation override) +// Behavioral SVF player for simple USER-chain simulation. +// +// Supported SVF commands (line-oriented, uppercase recommended): +// - SIR TDI (); +// - SDR TDI (); +// - RUNTEST TCK; +// - STATE RESET; +// - STATE IDLE; +// Other lines are ignored. +// ============================================================================= +module jtag_if #( + parameter integer chain = 1, + parameter [8*256-1:0] SVF_FILE = "write_jtag.svf", + parameter integer TCK_HALF_PERIOD_NS = 50, + parameter [31:0] USER_IR_OPCODE = 32'h00000002 +)( + input wire i_tdo, + output reg o_tck, + output reg o_tdi, + output reg o_drck, + output reg o_capture, + output reg o_shift, + output reg o_update, + output reg o_runtest, + output reg o_reset, + output reg o_sel +); + integer fd; + integer got; + integer nbits; + integer cycles; + integer i; + integer line_no; + integer scan_bits; + + reg [8*1024-1:0] line; + reg [8*32-1:0] cmd; + reg [8*32-1:0] arg1; + reg [8*256-1:0] svf_file_path; + reg [4095:0] scan_data; + reg [31:0] current_ir; + reg selected; + + // Keep linters quiet: TDO is not consumed by this simple player. + wire _unused_tdo; + assign _unused_tdo = i_tdo; + + task pulse_tck; + begin + #TCK_HALF_PERIOD_NS o_tck = 1'b1; + #TCK_HALF_PERIOD_NS o_tck = 1'b0; + end + endtask + + task pulse_drck_shift; + input bit_in; + begin + o_tdi = bit_in; + o_shift = 1'b1; + #TCK_HALF_PERIOD_NS begin + o_tck = 1'b1; + o_drck = 1'b1; + end + #TCK_HALF_PERIOD_NS begin + o_tck = 1'b0; + o_drck = 1'b0; + end + end + endtask + + task pulse_capture; + begin + o_capture = 1'b1; + #TCK_HALF_PERIOD_NS begin + o_tck = 1'b1; + o_drck = 1'b1; + end + #TCK_HALF_PERIOD_NS begin + o_tck = 1'b0; + o_drck = 1'b0; + o_capture = 1'b0; + end + end + endtask + + task pulse_update; + begin + o_shift = 1'b0; + #TCK_HALF_PERIOD_NS o_update = 1'b1; + #TCK_HALF_PERIOD_NS o_update = 1'b0; + end + endtask + + initial begin + // Default output levels + o_tck = 1'b0; + o_tdi = 1'b0; + o_drck = 1'b0; + o_capture = 1'b0; + o_shift = 1'b0; + o_update = 1'b0; + o_runtest = 1'b0; + o_reset = 1'b0; + o_sel = 1'b0; + selected = 1'b0; + current_ir = 32'h0; + line_no = 0; + svf_file_path = SVF_FILE; + + // Deterministic startup reset pulse before consuming SVF. + o_reset = 1'b1; + #TCK_HALF_PERIOD_NS o_tck = 1'b1; + #TCK_HALF_PERIOD_NS o_tck = 1'b0; + #TCK_HALF_PERIOD_NS o_tck = 1'b1; + #TCK_HALF_PERIOD_NS o_tck = 1'b0; + o_reset = 1'b0; + + fd = $fopen(svf_file_path, "r"); + if (fd == 0) begin + $display("jtag_if(sim): could not open SVF file '%0s'", svf_file_path); + end else begin + while (!$feof(fd)) begin + line = {8*1024{1'b0}}; + got = $fgets(line, fd); + line_no = line_no + 1; + if (got > 0) begin + cmd = {8*32{1'b0}}; + arg1 = {8*32{1'b0}}; + scan_data = {4096{1'b0}}; + nbits = 0; + cycles = 0; + + got = $sscanf(line, "%s", cmd); + if (got < 1) begin + // Empty line + end else if (cmd == "!") begin + // Comment line + end else if (cmd == "SIR") begin + got = $sscanf(line, "SIR %d TDI (%h);", nbits, scan_data); + if (got == 2) begin + current_ir = scan_data[31:0]; + selected = (current_ir == USER_IR_OPCODE); + o_sel = selected; + + // Emit TCK pulses for IR shift activity. + scan_bits = nbits; + if (scan_bits > 4096) scan_bits = 4096; + for (i = 0; i < scan_bits; i = i + 1) begin + o_tdi = scan_data[i]; + pulse_tck; + end + end + end else if (cmd == "SDR") begin + got = $sscanf(line, "SDR %d TDI (%h);", nbits, scan_data); + if (got == 2) begin + if (selected) begin + pulse_capture; + end + scan_bits = nbits; + if (scan_bits > 4096) scan_bits = 4096; + for (i = 0; i < scan_bits; i = i + 1) begin + pulse_drck_shift(scan_data[i]); + end + if (selected) begin + pulse_update; + end + end + end else if (cmd == "RUNTEST") begin + got = $sscanf(line, "RUNTEST %d TCK;", cycles); + if (got == 1) begin + o_runtest = 1'b1; + for (i = 0; i < cycles; i = i + 1) + pulse_tck; + o_runtest = 1'b0; + end + end else if (cmd == "STATE") begin + got = $sscanf(line, "STATE %s", arg1); + if (got == 1) begin + if (arg1 == "RESET;") begin + o_reset = 1'b1; + #TCK_HALF_PERIOD_NS o_reset = 1'b0; + selected = 1'b0; + o_sel = 1'b0; + end + end + end + end + end + $fclose(fd); + end + end +endmodule diff --git a/sim/tb/tb_svf.v b/sim/tb/tb_svf.v new file mode 100644 index 0000000..001c5d6 --- /dev/null +++ b/sim/tb/tb_svf.v @@ -0,0 +1,90 @@ +`timescale 1ns/1ps + +module tb_svf(); + wire [7:0] led_out; + wire [31:0] update_count; + + jtag_byte_sink #( + .SVF_FILE("sim/other/test.svf"), + .TCK_HALF_PERIOD_NS(50) + ) dut ( + .o_led(led_out), + .o_update_count(update_count) + ); + + initial begin + // Optional waveform dump. + $dumpfile("out.vcd"); + $dumpvars; + + // Timeout for large SVF playback. + #20_000; + $finish; + end +endmodule + +module jtag_byte_sink #( + parameter [8*256-1:0] SVF_FILE = "", + parameter integer TCK_HALF_PERIOD_NS = 50 +)( + output reg [7:0] o_led, + output reg [31:0] o_update_count +); + wire jtag_tck; + wire jtag_tdi; + wire jtag_drck; + wire jtag_capture; + wire jtag_shift; + wire jtag_update; + wire jtag_runtest; + wire jtag_reset; + wire jtag_sel; + wire jtag_tdo; + reg [7:0] shift_reg; + + assign jtag_tdo = shift_reg[0]; + + jtag_if #( + .chain(1), + .SVF_FILE(SVF_FILE), + .TCK_HALF_PERIOD_NS(TCK_HALF_PERIOD_NS), + .USER_IR_OPCODE(32'h0000_0002) + ) jtag ( + .i_tdo(jtag_tdo), + .o_tck(jtag_tck), + .o_tdi(jtag_tdi), + .o_drck(jtag_drck), + .o_capture(jtag_capture), + .o_shift(jtag_shift), + .o_update(jtag_update), + .o_runtest(jtag_runtest), + .o_reset(jtag_reset), + .o_sel(jtag_sel) + ); + + always @(posedge jtag_drck or posedge jtag_reset) begin + if (jtag_reset) begin + shift_reg <= 8'h00; + end else if (jtag_sel && jtag_capture) begin + shift_reg <= o_led; + end else if (jtag_sel && jtag_shift) begin + shift_reg <= {jtag_tdi, shift_reg[7:1]}; + end + end + + always @(posedge jtag_update or posedge jtag_reset) begin + if (jtag_reset) begin + o_led <= 8'h00; + o_update_count <= 32'd0; + end else if (jtag_sel) begin + o_led <= shift_reg; + o_update_count <= o_update_count + 1'b1; + end + end + + // Keep lint happy for currently unused outputs. + wire _unused_tck; + wire _unused_runtest; + assign _unused_tck = jtag_tck; + assign _unused_runtest = jtag_runtest; +endmodule diff --git a/sw/blinky/link.ld b/sw/blinky/link.ld index b14568d..483b1de 100644 --- a/sw/blinky/link.ld +++ b/sw/blinky/link.ld @@ -3,7 +3,7 @@ ENTRY(_start) MEMORY { - RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8K + RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8064 } SECTIONS diff --git a/sw/sweep/link.ld b/sw/sweep/link.ld index b14568d..483b1de 100644 --- a/sw/sweep/link.ld +++ b/sw/sweep/link.ld @@ -3,7 +3,7 @@ ENTRY(_start) MEMORY { - RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8K + RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 8064 } SECTIONS