123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440 |
- //
- // 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
|