Browse Source

Allow the CPU to force an FPGA reload; vjtag improvement

Add the ability for the CPU to force an FPGA reconfiguration from
flash, useful for updates.

Make the VJTAG interface a bit cleaner.
H. Peter Anvin 3 years ago
parent
commit
37de408c19

+ 41 - 0
fpga/fpgarst.sv

@@ -0,0 +1,41 @@
+//
+// FPGA reset control
+//
+// Interface with and control of the internal FPGA reset logic.
+//
+// Altera Cyclone IV E Remote Update block (cycloneiv_rublock):
+//
+// This block is a primitive, but there doesn't seem to be any
+// matching megafunction, except for the remote upgrade IP which
+// uses the on-chip active serial controller(?) and generally does
+// way more than we want.
+//
+// See the "Dedicated Remote System Upgrade Circuitry" section of the
+// Remote System Upgrade chapter (vol 1, chapter 8) of the Cyclone IV
+// manual.
+//
+// A refinement of this would be to allow shifting in a new boot address;
+// see the MAX 10 Configuration User Guide. for low-level protocol details
+// and the above reference for higher level protocol.
+//
+// At some point the Altera remote configuration IP is pretty much
+// what's needed anyway, though.
+//
+module fpgarst
+  (
+   input reconfig
+   );
+
+   // The internal primitive connecting to hard logic;
+   // ru stands for "remote update block"
+   cycloneive_rublock ru
+     (
+      .clk       ( 1'b0 ),	// Not using the shift register
+      .rconfig   ( reconfig ),	// Start reconfiguration
+      .rsttimer  ( 1'b1 ),	// Ignore watchdog timer
+      .regin     ( 1'bx ),	// Shift register data in
+      .regout	 ( ),		// Shift register data out
+      .captnupdt ( 1'b0 ),	// Capture/update#
+      .shiftnld  ( 1'b0 )	// Shift/load# shift register
+      );
+endmodule // fpgarst

File diff suppressed because it is too large
+ 1608 - 0
fpga/ip/altera_remote_update_core.v


+ 4 - 3
fpga/max80.qpf

@@ -19,15 +19,16 @@
 #
 # Quartus Prime
 # Version 21.1.0 Build 842 10/21/2021 SJ Lite Edition
-# Date created = 05:31:19  February 07, 2022
+# Date created = 17:04:28  February 09, 2022
 #
 # -------------------------------------------------------------------------- #
 
 QUARTUS_VERSION = "21.1"
-DATE = "05:31:19  February 07, 2022"
+DATE = "17:04:28  February 09, 2022"
 
 # Revisions
 
 PROJECT_REVISION = "v1"
-PROJECT_REVISION = "v2boot"
 PROJECT_REVISION = "v2"
+PROJECT_REVISION = "v2boot"
+PROJECT_REVISION = "v2alt"

+ 1 - 0
fpga/max80.qsf

@@ -246,5 +246,6 @@ set_global_assignment -name QIP_FILE ip/cdc_rxfifo.qip
 set_global_assignment -name SYSTEMVERILOG_FILE vjtag_max80.sv
 set_global_assignment -name VERILOG_FILE ip/vjtag/synthesis/vjtag.v
 set_global_assignment -name QIP_FILE ip/vjtag/synthesis/vjtag.qip
+set_global_assignment -name SYSTEMVERILOG_FILE fpgarst.sv
 
 set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

+ 17 - 10
fpga/max80.sv

@@ -135,9 +135,14 @@ module max80
    // -----------------------------------------------------------------------
    //   PLLs and reset
    // -----------------------------------------------------------------------
-   reg			    rst_n      = 1'b0;	// Internal system reset
-   reg			    hard_rst_n = 1'b0;	// Strict POR reset only
+   reg			    rst_n        = 1'b0; // Internal system reset
+   reg			    hard_rst_n   = 1'b0; // Strict POR reset only
+   reg			    reconfig_rst = 1'b0; // Reconfigure FPGA
 
+   fpgarst fpgarst ( .reconfig (reconfig_rst) );
+
+   assign reset_plls = 1'b0;
+   
    tri1 [4:1]		    pll_locked;
 
    assign   pll_locked[2] = master_pll_locked;
@@ -211,7 +216,6 @@ module max80
    always @(negedge all_plls_locked or posedge sys_clk)
      if (~all_plls_locked)
        begin
-	  reset_plls         <= 1'b0;
 	  hard_rst_n         <= 1'b0;
 	  rst_n              <= 1'b0;
 	  reset_cmd_q        <= 3'b0;
@@ -229,6 +233,9 @@ module max80
 	  reset_cmd_q <= (cpu_reset_cmd_q[0] & ~cpu_reset_cmd_q[1]) |
 			 aux_reset_cmd;
 
+	  if (reset_cmd_q[3])
+	    reconfig_rst <= 1'b1; // Force FPGA reconfiguration
+
 	  if (|reset_cmd_q)
 	    begin
 	       // Soft or hard reset
@@ -237,7 +244,6 @@ module max80
 	       sys_clk_stb   <= 1'b0;
 	       rst_n         <= 1'b0;
 	       hard_rst_n    <= hard_rst_n & ~|reset_cmd_q[3:2];
-	       reset_plls    <= reset_cmd_q[3];
 	    end
 	  else
 	    begin
@@ -273,6 +279,7 @@ module max80
    // Internal CPU bus
    //
    wire			      cpu_mem_valid;
+   wire 		      cpu_mem_ready;
    wire			      cpu_mem_instr;
    wire [ 3:0]		      cpu_mem_wstrb;
    wire [31:0]                cpu_mem_addr;
@@ -488,7 +495,7 @@ module max80
 	    | (cpu_irq & irq_edge_mask & ~(cpu_eoi & ~cpu_eoi_q));
        end
 
-   
+
    picorv32 #(
 	      .ENABLE_COUNTERS ( 1 ),
 	      .ENABLE_COUNTERS64 ( 1 ),
@@ -882,15 +889,15 @@ module max80
 
    // Virtual JTAG interface
    wire        vjtag_cpu_halt;
-   
+
    vjtag_max80 #(.sdram_base_addr(SDRAM_ADDR),
 		 .sdram_bits(SDRAM_BITS))
    vjtag (
 	  .rst_n	( rst_n ),
 	  .sys_clk      ( sdram_clk ),
-	  
+
 	  .sdram	( sr_bus[2].dstr ),
-	  
+
 	  .cpu_valid    ( iodev_valid_vjtag ),
 	  .cpu_addr     ( cpu_mem_addr[6:2] ),
 	  .cpu_wdata    ( cpu_mem_wdata ),
@@ -898,12 +905,12 @@ module max80
 	  .cpu_rdata    ( iodev_rdata_vjtag ),
 	  .cpu_irq      ( iodev_irq_vjtag ),
 	  .cpu_halt     ( vjtag_cpu_halt ),
- 
+
 	  .reset_cmd    ( vjtag_reset_cmd )
 	  );
 
    assign cpu_halt = vjtag_cpu_halt;
-   
+
    //
    // Registering of I/O data and handling of iodev_mem_ready
    //

+ 6 - 6
fpga/output/sram.mif

@@ -1747,12 +1747,12 @@ CONTENT BEGIN
 06CC : 6362612F;
 06CD : 6B736964;
 06CE : 3030382E;
-06CF : 6F4D002F;
-06D0 : 6546206E;
-06D1 : 37202062;
-06D2 : 3A353020;
-06D3 : 353A3532;
-06D4 : 53502031;
+06CF : 6557002F;
+06D0 : 65462064;
+06D1 : 39202062;
+06D2 : 3A373120;
+06D3 : 313A3230;
+06D4 : 53502030;
 06D5 : 30322054;
 06D6 : 003232;
 [06D7..1FFF] : 00;

BIN
fpga/output/v1.jic


BIN
fpga/output/v1.rbf.gz


BIN
fpga/output/v1.rpd.gz


BIN
fpga/output/v1.sof


BIN
fpga/output/v1.svf.gz


BIN
fpga/output/v1.xsvf.gz


BIN
fpga/output/v2.jic


BIN
fpga/output/v2.rbf.gz


BIN
fpga/output/v2.rpd.gz


BIN
fpga/output/v2.sof


BIN
fpga/output/v2.svf.gz


BIN
fpga/output/v2.xsvf.gz


BIN
fpga/output/v2boot.rbf.gz


BIN
fpga/output/v2boot.sof


BIN
fpga/output/v2boot.svf.gz


BIN
fpga/output/v2boot.xsvf.gz


+ 0 - 1
fpga/v2.vh

@@ -120,7 +120,6 @@ module `TOP
    wire		  reset_plls;
    wire		  master_pll_locked;
    wire		  master_clk;
-   wire 	  master_clk_48;
 
    pll2_16 pll2 (
 		 .areset ( reset_plls ),

+ 374 - 0
fpga/vjtag_max80.sv

@@ -0,0 +1,374 @@
+//
+// 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
+// 1011x - write data to memory
+// 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 [31:0] sdram_base_addr,
+   parameter        sdram_bits
+) (
+   input 	 rst_n,
+   input 	 sys_clk,
+		 
+   dram_bus.dstr sdram,
+
+   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,
+
+   output reg 	 reset_cmd
+		);
+
+   wire 	  v_tdi;
+   wire		  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;
+   reg  [31:0] jtag_shr;
+   reg	       tdi_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)
+   reg 	       st_cdr_s;
+   reg 	       st_sdr_s;
+   reg 	       st_xdr_s;	// Any state between CDR and UDR, exclusively
+   reg 	       st_udr_s;
+   reg 	       st_uir_s;
+   
+   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];
+	st_cdr_s    <= v_st_cdr;
+	st_sdr_s    <= v_st_sdr;
+	st_xdr_s    <= v_st_sdr|v_st_e1dr|v_st_e2dr|v_st_pdr;
+	st_udr_s    <= v_st_udr;
+	st_uir_s    <= v_st_uir;
+     end
+
+   assign v_tdo = jtag_bypass ? tdi_s : jtag_shr[0];
+
+   // 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)) 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)
+   localparam [31:0] memaddr_mask = (1'b1 << sdram_bits) - 3'b100;
+
+   function logic [31:0] maskaddr (input [31:0] addr);
+      maskaddr = (addr & memaddr_mask) | sdram_base_addr;
+   endfunction
+   
+   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] mem_rdata;
+   reg  [31:0] mem_wdata;
+   wire        mem_ready;
+   reg 	       mem_done;
+   reg 	       mem_error;	// Memory underrun
+
+   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;
+   
+   always @(posedge sys_clk)
+     begin
+	if ( ~rst_n )
+	  jtag_reset_cmd <= 1'b0;
+	
+	jtag_cpu_irq <= 1'b0;
+
+	if ( tck_stb )
+	  begin
+	     if ( st_sdr_s )
+	       begin
+		  if ( ir_cmd[3] )
+		    jtag_shr <= { tdi_s, jtag_shr[31:1] };
+		  else
+		    jtag_shr <= { 30'bx, tdi_s, jtag_shr[1] };
+	       end
+	     
+	     if ( st_cdr_s )
+	       case ( ir_cmd )
+		 cmd_halt:      jtag_shr[0] <= jtag_cpu_halt;
+		 cmd_memerr: begin
+		    jtag_shr[0] <= mem_error;
+		    if ( ~ir_ro ) mem_error <= 1'b0;
+		 end
+		 cmd_mem0, cmd_memaddr, cmd_memread, cmd_memwrite:
+		   jtag_shr <= jtag_memaddr;
+		 cmd_cpucmd, cmd_cpucmd_irq:
+		   jtag_shr  <= jtag_cpucmd;
+		 cmd_cpuinfo:    jtag_shr    <= jtag_cpuinfo;
+		 cmd_cpustatus:  jtag_shr    <= jtag_cpustatus;
+		 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.
+
+	     if ( ir_cmd[3:1] == cmd_memwr )
+	       begin
+		  if ( st_cdr_s )
+		    begin
+		       mem_done           <= 1'b0;
+		       mem_valid          <= 1'b0;
+		       mem_write          <= ir_cmd[0];
+		    end
+
+		  if ( ~st_xdr_s )
+		    begin
+		       sdr_ctr            <= 5'b0;
+		       mem_addr           <= jtag_memaddr;
+		    end
+
+		  if ( st_sdr_s )
+		    begin
+		       sdr_ctr <= sdr_ctr + 1'b1;
+
+		       // Applicable to both read and write
+		       case ( sdr_ctr )
+			 5'd31: begin
+			    if ( mem_valid )
+			      mem_error <= 1'b1; // Underrun!
+			 end
+			 default: ;
+		       endcase // case ( sdr_ctr )
+
+		       if ( ~mem_write )
+			 // Read
+			 case ( sdr_ctr )
+			   5'd1: begin
+			      // For a read, make sure we are committed
+			      // to reading the new word
+			      if ( ~ir_ro )
+				jtag_memaddr     <= mem_addr;
+			      mem_valid          <= 1'b1;
+			      mem_done           <= 1'b0;
+			   end
+			   5'd31: begin
+			      jtag_shr <= mem_rdata;
+			   end
+			   default: ;
+			 endcase // case ( sdr_ctr )
+		       else
+			 // Write
+			 case ( sdr_ctr )
+			   5'd31: begin
+			      mem_wdata          <= jtag_shr;
+			      mem_valid          <= 1'b1;
+			      mem_done           <= 1'b0;
+			      if ( ~ir_ro )
+				jtag_memaddr     <= mem_addr_next;
+			   end
+			   default: ;
+			 endcase // case ( sdr_ctr )
+		    end // if ( st_sdr_s )
+	       end // if ( ir_cmd[3:1] == cmd_memwr )
+
+	     if ( st_uir_s )
+	       jtag_cpu_irq <= ir_cmd == cmd_irq;
+	     else if ( st_udr_s )
+	       jtag_cpu_irq <= ir_cmd == cmd_cpucmd_irq;
+
+	     if ( st_uir_s )
+	       jtag_reset_cmd <= jtag_reset_cmd | (ir_cmd == cmd_reset);
+	     
+	     if ( st_udr_s & ~ir_ro )
+	       case ( ir_cmd )
+		 cmd_halt:    jtag_cpu_halt  <= jtag_shr[0];
+		 cmd_memaddr: jtag_memaddr   <= maskaddr(jtag_shr);
+		 cmd_cpucmd, cmd_cpucmd_irq:
+		   jtag_cpucmd    <= jtag_shr;
+		 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 & mem_ready )
+	  begin
+	     mem_valid <= 1'b0;
+	     mem_done  <= 1'b1;
+	     mem_addr  <= mem_addr_next;
+	  end
+     end
+   
+   dram_port #(32) mem
+     (
+      .bus   ( sdram ),
+      .prio  ( 2'd2 ),
+      .addr  ( mem_addr ),
+      .valid ( mem_valid ),
+      .wd    ( mem_wdata ),
+      .wstrb ( {4{mem_write}} ),
+      .ready ( mem_ready ),
+      .rd    ( mem_rdata )
+      );
+
+   wire [7:0] cpustatus_new =
+	      ( tck_stb & st_cdr_s & ~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

Some files were not shown because too many files changed in this diff