//
// 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  [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
	     if ( v_st_sdr )
	       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_in[0];
		 end
		 cmd_memaddr: begin
		    jtag_memaddr   <= maskaddr(jtag_shr_in);
		 end
		 cmd_cpucmd, cmd_cpucmd_irq: begin
		    jtag_cpucmd    <= jtag_shr_in;
		 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
