// // vjtag_max80.sv // // Access the SDRAM and allow the CPU to take interrupts and receive // a command opcode via virtual JTAG. // // The module supports the following command codes over virtual JTAG: // // These chains use a common 1-bit boolean register: // // 00000 - bypass // 00010 - trigger IRQ on update_IR // 00100 - trigger system (soft) reset on update_IR // 0011x - assert halt while bit set // 0100x - memory underrun status flag, clear on capture // // These chains use a common 32-bit shift register: // // 1001x - address register (only bits [24:2] settable) // 1010x - read data from memory // returns address before streaming 32-bit data words // 1011x - write data to memory // send VJTAG_WRITE_PREFIX before streaming 32-bit data words // 1100x - command register to CPU // 1101x - command register to CPU, trigger IRQ on update_DR // 1110x - info register from CPU // 1111x - status register from CPU, clear on capture // // Setting bit 0 suppresses all register updates (but not other // side effects), allowing for nondestructive reads. // // The CPU registers are: // // 0 - CPU command register (readonly) // 1 - CPU information register (rw) // 2 - CPU status register (rw) // 3 - CPU status register set bits (rw1) // module vjtag_max80 #( parameter sram_bits, parameter [31:0] sdram_base_addr, parameter sdram_bits, parameter [31:0] VJTAG_WRITE_PREFIX = 32'hABC80FED ) ( input rst_n, input sys_clk, output reset_cmd, input cpu_valid, input [6:2] cpu_addr, input [31:0] cpu_wdata, input [ 3:0] cpu_wstrb, output [31:0] cpu_rdata, output cpu_irq, output cpu_halt, dram_bus.dstr sdram, // SRAM interface output [sram_bits-1:2] sram_addr, input [31:0] sram_rdata, output [31:0] sram_wdata, output sram_read, output sram_write ); wire v_tdi; reg v_tdo; wire [4:0] v_ir; wire v_tck; wire v_st_cdr; wire v_st_sdr; wire v_st_e1dr; wire v_st_pdr; wire v_st_e2dr; wire v_st_udr; wire v_st_cir; wire v_st_uir; vjtag vjtag ( .tdi ( v_tdi ), .tdo ( v_tdo ), .tck ( v_tck ), // Really nothing virtual... .ir_in ( v_ir ), .ir_out ( ), .virtual_state_cdr ( v_st_cdr ), .virtual_state_e1dr ( v_st_e1dr ), .virtual_state_e2dr ( v_st_e2dr ), .virtual_state_pdr ( v_st_pdr ), .virtual_state_sdr ( v_st_sdr ), .virtual_state_udr ( v_st_udr ), .virtual_state_uir ( v_st_uir ), .virtual_state_cir ( v_st_cir ) ); localparam cmd_bypass = 4'b0000; localparam cmd_irq = 4'b0001; localparam cmd_reset = 4'b0010; localparam cmd_halt = 4'b0011; localparam cmd_memerr = 4'b0100; localparam cmd_mem0 = 4'b1000; localparam cmd_memaddr = 4'b1001; localparam cmd_memread = 4'b1010; localparam cmd_memwrite = 4'b1011; localparam cmd_memwr = 3'b101; // Common bits of read and write localparam cmd_cpucmd = 4'b1100; localparam cmd_cpucmd_irq = 4'b1101; // cpucmd but trigger IRQ localparam cmd_cpuinfo = 4'b1110; localparam cmd_cpustatus = 4'b1111; reg jtag_bypass; wire jtag_out; reg tdi_s; wire tdo_s; // Latched information for use in the synchronous state machine reg [3:0] ir_cmd; // Command part of IR reg ir_ro; // Readonly (update suppress) always @(posedge v_tck) begin jtag_bypass <= v_ir == cmd_bypass; tdi_s <= v_tdi; ir_cmd <= v_ir[4:1]; ir_ro <= v_ir[0]; end assign v_tdo = jtag_bypass ? tdi_s : tdo_s; // Sync incoming JTAG signals. Only tck needs an actual // synchronizer; the rest just need holding registers (see above) // as the delay of tck will guarantee the others are stable. wire tck_s; synchronizer #(.width(1), .stages(3)) tck_sync ( .rst_n ( rst_n ), .clk ( sys_clk ), .d ( v_tck ), .q ( tck_s ) ); // Mask of memaddr bits that are not settable: the top bits // and the bottom two bits (byte within dword), and the // sram/dram select bit localparam [31:0] memaddr_mask = ((1'b1 << sdram_bits) - 3'b100) | sdram_base_addr; function logic [31:0] maskaddr (input [31:0] addr); maskaddr = addr & memaddr_mask; endfunction wire is_dram = |(mem_addr & sdram_base_addr); // Really just one bit reg jtag_cpu_irq = 1'b0; reg jtag_cpu_halt = 1'b0; reg jtag_reset_cmd = 1'b0; assign cpu_irq = jtag_cpu_irq; assign cpu_halt = jtag_cpu_halt; assign reset_cmd = jtag_reset_cmd; reg [31:0] jtag_memaddr = maskaddr(32'b0); reg [31:0] jtag_cpucmd; reg [31:0] jtag_cpuinfo; reg [ 7:0] jtag_cpustatus; reg mem_valid; reg mem_write; reg [31:0] mem_addr; wire [31:0] mem_addr_next = maskaddr(mem_addr + 3'h4); wire [31:0] sdram_rdata; reg [31:0] mem_wdata; wire sdram_ready; reg sram_ready; reg mem_done; reg mem_error; // Memory underrun reg advance_mem_addr; reg mem_header_done; reg mem_do_write; reg tck_q; reg tck_stb; always @(posedge sys_clk) begin tck_q <= tck_s; tck_stb <= tck_s & ~tck_q; end // Keep a counter to keep track of SDRAM data bit count; this is to // allow streaming of data to/from SDRAM without leaving the // SDR state. reg [4:0] sdr_ctr; // Main data shift register. reg v_st_sdr_q; reg [31:0] jtag_shr; wire [31:0] jtag_shr_in = ir_cmd[3] ? { tdi_s, jtag_shr[31:1] } : { 30'bx, tdi_s, jtag_shr[1] }; assign tdo_s = jtag_shr[0]; always @(posedge sys_clk) begin if ( ~rst_n ) jtag_reset_cmd <= 1'b0; jtag_cpu_irq <= 1'b0; if ( tck_stb ) begin v_st_sdr_q <= v_st_sdr; if ( v_st_sdr_q ) jtag_shr <= jtag_shr_in; if ( v_st_cdr ) case ( ir_cmd ) cmd_halt: begin jtag_shr[0] <= jtag_cpu_halt; end cmd_memerr: begin jtag_shr[0] <= mem_error; if ( ~ir_ro ) mem_error <= 1'b0; end cmd_mem0, cmd_memaddr, cmd_memread, cmd_memwrite: begin jtag_shr <= jtag_memaddr; end cmd_cpucmd, cmd_cpucmd_irq: begin jtag_shr <= jtag_cpucmd; end cmd_cpuinfo: begin jtag_shr <= jtag_cpuinfo; end cmd_cpustatus: begin jtag_shr <= jtag_cpustatus; end default: ; endcase // case ( ir_cmd ) // For performance, the SDRAM data can be streamed // without exiting the shift_DR state, so this is // based on a counter rather than going to the DR_update // state. // // Note: for cmd_memread, this will trigger an // unnecessary memory load at the end, but that is // completely harmless, and we cannot know until the clock // comes in if we need it or not, so we have to // suppress the update until we know that the user will // be reading the fetched data. mem_do_write <= 1'b0; if ( ir_cmd[3:1] == cmd_memwr ) begin if ( v_st_cdr ) begin mem_done <= 1'b0; mem_valid <= 1'b0; mem_write <= ir_cmd[0]; advance_mem_addr <= 1'b0; mem_header_done <= 1'b0; mem_addr <= jtag_memaddr; sdr_ctr <= 5'b0; mem_do_write <= 1'b0; end if ( v_st_sdr ) begin sdr_ctr <= sdr_ctr + 1'b1; if ( ~mem_header_done ) begin if ( ~mem_write ) begin // Read mem_header_done <= &sdr_ctr; end else begin // Write if ( jtag_shr_in == VJTAG_WRITE_PREFIX ) mem_header_done <= 1'b1; else sdr_ctr <= 5'b0; end end // Memory access underrun? if ( sdr_ctr == 5'd31 ) mem_error <= mem_error | mem_valid; if ( ~mem_write ) begin // Read case ( sdr_ctr ) 5'd2: begin // For a read, make sure we are committed // to reading the new word if ( ~ir_ro ) jtag_memaddr <= mem_addr; advance_mem_addr <= mem_header_done; end 5'd3: begin // After mem_addr advanced mem_valid <= 1'b1; mem_done <= 1'b0; end 5'd31: begin jtag_shr <= is_dram ? sdram_rdata : sram_rdata; end default: ; endcase // case ( sdr_ctr ) end // if ( ~mem_write ) else begin // Write mem_do_write <= &sdr_ctr; end // else: !if( ~mem_write ) end // if ( st_sdr_s ) end // if ( ir_cmd[3:1] == cmd_memwr ) if ( mem_do_write ) begin mem_wdata <= jtag_shr_in; mem_valid <= 1'b1; mem_done <= 1'b0; advance_mem_addr <= 1'b1; if ( ~ir_ro ) jtag_memaddr <= mem_addr_next; end if ( v_st_uir ) jtag_cpu_irq <= ir_cmd == cmd_irq; else if ( v_st_udr ) jtag_cpu_irq <= ir_cmd == cmd_cpucmd_irq; if ( v_st_uir ) jtag_reset_cmd <= jtag_reset_cmd | (ir_cmd == cmd_reset); if ( v_st_udr & ~ir_ro ) case ( ir_cmd ) cmd_halt: begin jtag_cpu_halt <= jtag_shr[0]; end cmd_memaddr: begin jtag_memaddr <= maskaddr(jtag_shr); end cmd_cpucmd, cmd_cpucmd_irq: begin jtag_cpucmd <= jtag_shr; end default: /* nothing */ ; endcase // case ( ir_cmd ) end // if ( tck_stb ) // Increment the temporary address register if applicable, // but only after the previous transaction is done... if ( mem_valid ) begin if (is_dram ? sdram_ready : sram_ready) begin mem_valid <= 1'b0; mem_done <= 1'b1; end else sram_ready <= ~is_dram; end if ( advance_mem_addr & ~mem_valid ) begin mem_addr <= mem_addr_next; advance_mem_addr <= 1'b0; end end dram_port #(32) mem ( .bus ( sdram ), .prio ( 2'd2 ), .addr ( mem_addr ), .valid ( mem_valid & is_dram ), .wd ( mem_wdata ), .wstrb ( {4{mem_write}} ), .ready ( sdram_ready ), .rd ( sdram_rdata ) ); assign sram_addr = mem_addr[sram_bits-1:2]; assign sram_wdata = mem_wdata; assign sram_read = mem_valid & ~is_dram & ~mem_write; assign sram_write = mem_valid & ~is_dram & mem_write; wire [7:0] cpustatus_new = ( tck_stb & v_st_cdr & ~ir_ro & (ir_cmd == cmd_cpustatus) ) ? 'b0 : jtag_cpustatus; always @(negedge rst_n or posedge sys_clk) if (~rst_n) begin jtag_cpuinfo <= 'b0; jtag_cpustatus <= 'b0; end else begin jtag_cpustatus <= cpustatus_new; if ( cpu_valid ) begin case ( cpu_addr ) 5'b00001: begin if ( cpu_wstrb[0] ) jtag_cpuinfo[7:0] <= cpu_wdata[7:0]; if ( cpu_wstrb[1] ) jtag_cpuinfo[15:8] <= cpu_wdata[15:8]; if ( cpu_wstrb[2] ) jtag_cpuinfo[23:16] <= cpu_wdata[23:16]; if ( cpu_wstrb[3] ) jtag_cpuinfo[31:24] <= cpu_wdata[31:24]; end 5'b00010: begin if ( cpu_wstrb[0] ) jtag_cpustatus <= cpu_wdata[7:0]; // Add more if the width of jtag_cpustatus is increased end 5'b00011: begin if ( cpu_wstrb[0] ) jtag_cpustatus <= cpustatus_new | cpu_wdata[7:0]; end default: /* nothing */; endcase // case ( cpu_addr[1:0] ) end // if ( cpu_valid ) end always @(*) casez ( cpu_addr ) 5'b00000: cpu_rdata = jtag_cpucmd; 5'b00001: cpu_rdata = jtag_cpuinfo; 5'b0001?: cpu_rdata = jtag_cpustatus; default: cpu_rdata = 32'bx; endcase // casez ( cpu_addr ) endmodule // vjtag_max80