Преглед изворни кода

Full infrastructure for updating flash via JTAG SVF

Now have full support for smart updating of the flash via JTAG
SVF... or for that matter, any other method (should add from SD card
next - trivial application of the same tools.)
H. Peter Anvin пре 3 година
родитељ
комит
ee45852b85
53 измењених фајлова са 769 додато и 460 уклоњено
  1. 3 0
      fpga/.gitignore
  2. 27 9
      fpga/Makefile
  3. 4 2
      fpga/fpgarst.sv
  4. 37 9
      fpga/ip/pll2_16.v
  5. 37 9
      fpga/ip/pll2_48.v
  6. 1 1
      fpga/jic.cof.xml
  7. 3 3
      fpga/max80.qpf
  8. 2 0
      fpga/max80.qsf
  9. 100 87
      fpga/max80.sv
  10. BIN
      fpga/output/jtagupd/v1.rbf.gz
  11. BIN
      fpga/output/jtagupd/v1.sof
  12. BIN
      fpga/output/jtagupd/v1.svf.gz
  13. BIN
      fpga/output/jtagupd/v2.rbf.gz
  14. BIN
      fpga/output/jtagupd/v2.sof
  15. BIN
      fpga/output/jtagupd/v2.svf.gz
  16. BIN
      fpga/output/v1.fw
  17. BIN
      fpga/output/v1.jic
  18. BIN
      fpga/output/v1.rbf.gz
  19. BIN
      fpga/output/v1.rpd.gz
  20. BIN
      fpga/output/v1.sof
  21. BIN
      fpga/output/v1.svf.gz
  22. BIN
      fpga/output/v1.update.svf.gz
  23. BIN
      fpga/output/v1.update.xsvf.gz
  24. BIN
      fpga/output/v1.xsvf.gz
  25. BIN
      fpga/output/v2.fw
  26. BIN
      fpga/output/v2.jic
  27. BIN
      fpga/output/v2.rbf.gz
  28. BIN
      fpga/output/v2.rpd.gz
  29. BIN
      fpga/output/v2.sof
  30. BIN
      fpga/output/v2.svf.gz
  31. BIN
      fpga/output/v2.update.svf.gz
  32. BIN
      fpga/output/v2.update.xsvf.gz
  33. BIN
      fpga/output/v2.xsvf.gz
  34. 1 1
      fpga/pof.cof.xml
  35. 8 1
      fpga/scripts/flashsvf.pl
  36. 50 47
      fpga/spirom.sv
  37. 63 61
      fpga/v1.sv
  38. 5 2
      fpga/v2.vh
  39. 6 4
      fpga/vjtag_max80.sv
  40. 1 1
      rv32/Makefile
  41. 1 1
      rv32/checksum.h
  42. 0 1
      rv32/die.c
  43. 21 4
      rv32/io.h
  44. 1 0
      rv32/ioregs.h
  45. 8 2
      rv32/irqasm.S
  46. 12 11
      rv32/jtagupd.c
  47. 10 0
      rv32/jtagupd.ld
  48. 3 0
      rv32/max80.ld
  49. 33 47
      rv32/romcopy.c
  50. 270 153
      rv32/spiflash.c
  51. 60 2
      rv32/spiflash.h
  52. 1 1
      rv32/sys.h
  53. 1 1
      tools/wrapflash.pl

+ 3 - 0
fpga/.gitignore

@@ -12,6 +12,9 @@ mif/
 *.cof
 *.svf
 *.rbf
+*.rrb
+*.rpf
+*.rpf.gz
 *.rpd
 *.xsvf
 *.cdf

+ 27 - 9
fpga/Makefile

@@ -35,11 +35,13 @@ PREREQFILES = $(mifdir)/sram.mif \
 	      $(foreach rev,$(REVISIONS),$(foreach coffmt,jic pof, \
 		$(outdir)/$(rev).$(coffmt).cof))
 
-alltarg := sof pof jic svf svf.gz xsvf xsvf.gz rbf rbf.gz \
-	   rpd rpd.gz pow.rpt sta.rpt
+alltarg := sof pof jic svf svf.gz xsvf xsvf.gz rbf rbf.gz rpf rpf.gz \
+	   rpd rpd.gz fw pow.rpt sta.rpt
 allout   = $(foreach o,$(alltarg),$(outdir)/$(1).$(o))
 vartarg := sof svf svf.gz rbf rbf.gz
 varout   = $(foreach o,$(vartarg),$(outdir)/$(1).$(o))
+uptarg  := update.svf update.svf.gz update.xsvf update.xsvf.gz
+upout    = $(foreach o,$(uptarg),$(outdir)/$(1).$(o))
 
 sram_src = ../rv32/
 
@@ -53,6 +55,7 @@ all:
 	$(MAKE) prereq
 	$(MAKE) $(REVISIONS:=.targets)
 	$(MAKE) $(VARIANTS)
+	$(MAKE) $(REVISIONS:=.update)
 
 -include $(REVISIONS:=.deps)
 
@@ -65,6 +68,10 @@ $(REVISIONS):
 %.targets:
 	$(MAKE) $(call allout,$*)
 
+.PHONY: %.update
+%.update:
+	$(MAKE) $(call upout,$*)
+
 .PHONY: $(VARIANTS)
 $(VARIANTS):
 	$(MAKE) $(outdir)/$@/variant.stamp
@@ -117,10 +124,10 @@ $(foreach rev,$(REVISIONS),$(outdir)/$(rev).%.cof): %.cof.xml
 	$(SED) -e 's/@@PROJECT@@/$(@F:.$*.cof=)/g' $< > $@
 
 $(outdir)/%.jic: $(outdir)/%.jic.cof $(outdir)/%.sof ../rv32/dram.hex
-	$(QCPF) -c $<
+	$(QCPF) -o bitstream_compression=on -c $<
 
 $(outdir)/%.pof: $(outdir)/%.pof.cof $(outdir)/%.sof
-	$(QCPF) -c $<
+	$(QCPF) -o bitstream_compression=on -c $<
 
 # This produces a transient-load .svf file
 $(outdir)/%.svf: $(outdir)/%.sof
@@ -131,20 +138,31 @@ $(outdir)/%.svf: $(outdir)/%.sof
 $(outdir)/%.xsvf: $(outdir)/%.svf ../tools/svf2xsvf.py
 	$(PYTHON) ../tools/svf2xsvf.py $< $@
 
-# Raw Binary File, compact data for transient programming or for loading
-# into flash (address 0); does *not* include the non-FPGA code; load
-# ../rv32/dram.{bin,bin.gz,hex} for that.
+# Raw Binary File, compact data for transient programming.
 $(outdir)/%.rbf: $(outdir)/%.sof
 	$(QCPF) -c $< $@
 
 # Raw Programmer Data, for loading into flash, includes all contents
-# but is rather large if not compressed.
+# but is rather large if not compressed. Adjust bit ordering to match
+# SPI...
 $(outdir)/%.rpd: $(outdir)/%.pof
-	$(QCPF) -c $< $@
+	$(QCPF) -o rpd_little_endian=off -o bitstream_compression=on -c $< $@
+
+# RPD file for the FPGA only
+$(outdir)/%.rpf: $(outdir)/%.rpd $(outdir)/%.rbf
+	dd if=$< of=$@ bs=$$(wc -c < $(@:.rpf=.rbf)) count=1
 
 $(outdir)/%.gz: $(outdir)/%
 	$(GZIP) -9 < $< > $@
 
+$(outdir)/%.fw: $(outdir)/%.rpf.gz ../rv32/dram.bin.gz ../tools/wrapflash.pl
+	$(PERL) ../tools/wrapflash.pl $@ $< 0 ../rv32/dram.bin.gz 0x100000
+
+
+$(outdir)/%.update.svf: ./scripts/flashsvf.pl \
+	$(outdir)/jtagupd/%.svf $(outdir)/%.map.rpt $(outdir)/%.fw
+	$(PERL) $^ $@
+
 # Prerequisite directories and files
 .PHONY: prereq
 prereq:

+ 4 - 2
fpga/fpgarst.sv

@@ -23,6 +23,8 @@
 //
 module fpgarst
   (
+   input rst_n,
+   input clk,
    input reconfig
    );
 
@@ -30,9 +32,9 @@ module fpgarst
    // ru stands for "remote update block"
    cycloneive_rublock ru
      (
-      .clk       ( 1'b0 ),	// Not using the shift register
+      .clk       ( clk ),	// Is this even needed?
       .rconfig   ( reconfig ),	// Start reconfiguration
-      .rsttimer  ( 1'b1 ),	// Ignore watchdog timer
+      .rsttimer  ( 1'b0 ),	// Don't reset the timer
       .regin     ( 1'bx ),	// Shift register data in
       .regout	 ( ),		// Shift register data out
       .captnupdt ( 1'b0 ),	// Capture/update#

+ 37 - 9
fpga/ip/pll2_16.v

@@ -41,11 +41,13 @@ module pll2_16 (
 	areset,
 	inclk0,
 	c0,
+	c1,
 	locked);
 
 	input	  areset;
 	input	  inclk0;
 	output	  c0;
+	output	  c1;
 	output	  locked;
 `ifndef ALTERA_RESERVED_QIS
 // synopsys translate_off
@@ -56,19 +58,21 @@ module pll2_16 (
 `endif
 
 	wire [4:0] sub_wire0;
-	wire  sub_wire2;
-	wire [0:0] sub_wire5 = 1'h0;
+	wire  sub_wire3;
+	wire [0:0] sub_wire6 = 1'h0;
+	wire [1:1] sub_wire2 = sub_wire0[1:1];
 	wire [0:0] sub_wire1 = sub_wire0[0:0];
 	wire  c0 = sub_wire1;
-	wire  locked = sub_wire2;
-	wire  sub_wire3 = inclk0;
-	wire [1:0] sub_wire4 = {sub_wire5, sub_wire3};
+	wire  c1 = sub_wire2;
+	wire  locked = sub_wire3;
+	wire  sub_wire4 = inclk0;
+	wire [1:0] sub_wire5 = {sub_wire6, sub_wire4};
 
 	altpll	altpll_component (
 				.areset (areset),
-				.inclk (sub_wire4),
+				.inclk (sub_wire5),
 				.clk (sub_wire0),
-				.locked (sub_wire2),
+				.locked (sub_wire3),
 				.activeclock (),
 				.clkbad (),
 				.clkena ({6{1'b1}}),
@@ -108,6 +112,10 @@ module pll2_16 (
 		altpll_component.clk0_duty_cycle = 50,
 		altpll_component.clk0_multiply_by = 21,
 		altpll_component.clk0_phase_shift = "0",
+		altpll_component.clk1_divide_by = 4,
+		altpll_component.clk1_duty_cycle = 50,
+		altpll_component.clk1_multiply_by = 3,
+		altpll_component.clk1_phase_shift = "0",
 		altpll_component.inclk0_input_frequency = 62500,
 		altpll_component.intended_device_family = "Cyclone IV E",
 		altpll_component.lpm_hint = "CBX_MODULE_PREFIX=pll2_16",
@@ -140,7 +148,7 @@ module pll2_16 (
 		altpll_component.port_scanread = "PORT_UNUSED",
 		altpll_component.port_scanwrite = "PORT_UNUSED",
 		altpll_component.port_clk0 = "PORT_USED",
-		altpll_component.port_clk1 = "PORT_UNUSED",
+		altpll_component.port_clk1 = "PORT_USED",
 		altpll_component.port_clk2 = "PORT_UNUSED",
 		altpll_component.port_clk3 = "PORT_UNUSED",
 		altpll_component.port_clk4 = "PORT_UNUSED",
@@ -181,8 +189,11 @@ endmodule
 // Retrieval info: PRIVATE: CUR_FBIN_CLK STRING "c0"
 // Retrieval info: PRIVATE: DEVICE_SPEED_GRADE STRING "Any"
 // Retrieval info: PRIVATE: DIV_FACTOR0 NUMERIC "1"
+// Retrieval info: PRIVATE: DIV_FACTOR1 NUMERIC "4"
 // Retrieval info: PRIVATE: DUTY_CYCLE0 STRING "50.00000000"
+// Retrieval info: PRIVATE: DUTY_CYCLE1 STRING "50.00000000"
 // Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE0 STRING "336.000000"
+// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE1 STRING "12.000000"
 // Retrieval info: PRIVATE: EXPLICIT_SWITCHOVER_COUNTER STRING "0"
 // Retrieval info: PRIVATE: EXT_FEEDBACK_RADIO STRING "0"
 // Retrieval info: PRIVATE: GLOCKED_COUNTER_EDIT_CHANGED STRING "1"
@@ -203,18 +214,26 @@ endmodule
 // Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE STRING "Not Available"
 // Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE_DIRTY NUMERIC "0"
 // Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT0 STRING "deg"
+// Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT1 STRING "ps"
 // Retrieval info: PRIVATE: MIG_DEVICE_SPEED_GRADE STRING "Any"
 // Retrieval info: PRIVATE: MIRROR_CLK0 STRING "0"
+// Retrieval info: PRIVATE: MIRROR_CLK1 STRING "0"
 // Retrieval info: PRIVATE: MULT_FACTOR0 NUMERIC "21"
+// Retrieval info: PRIVATE: MULT_FACTOR1 NUMERIC "3"
 // Retrieval info: PRIVATE: NORMAL_MODE_RADIO STRING "0"
 // Retrieval info: PRIVATE: OUTPUT_FREQ0 STRING "336.00000000"
+// Retrieval info: PRIVATE: OUTPUT_FREQ1 STRING "12.00000000"
 // Retrieval info: PRIVATE: OUTPUT_FREQ_MODE0 STRING "0"
+// Retrieval info: PRIVATE: OUTPUT_FREQ_MODE1 STRING "0"
 // Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT0 STRING "MHz"
+// Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT1 STRING "MHz"
 // Retrieval info: PRIVATE: PHASE_RECONFIG_FEATURE_ENABLED STRING "1"
 // Retrieval info: PRIVATE: PHASE_RECONFIG_INPUTS_CHECK STRING "0"
 // Retrieval info: PRIVATE: PHASE_SHIFT0 STRING "0.00000000"
+// Retrieval info: PRIVATE: PHASE_SHIFT1 STRING "0.00000000"
 // Retrieval info: PRIVATE: PHASE_SHIFT_STEP_ENABLED_CHECK STRING "0"
 // Retrieval info: PRIVATE: PHASE_SHIFT_UNIT0 STRING "deg"
+// Retrieval info: PRIVATE: PHASE_SHIFT_UNIT1 STRING "ps"
 // Retrieval info: PRIVATE: PLL_ADVANCED_PARAM_CHECK STRING "0"
 // Retrieval info: PRIVATE: PLL_ARESET_CHECK STRING "1"
 // Retrieval info: PRIVATE: PLL_AUTOPLL_CHECK NUMERIC "1"
@@ -237,11 +256,14 @@ endmodule
 // Retrieval info: PRIVATE: SPREAD_USE STRING "0"
 // Retrieval info: PRIVATE: SRC_SYNCH_COMP_RADIO STRING "0"
 // Retrieval info: PRIVATE: STICKY_CLK0 STRING "1"
+// Retrieval info: PRIVATE: STICKY_CLK1 STRING "1"
 // Retrieval info: PRIVATE: SWITCHOVER_COUNT_EDIT NUMERIC "1"
 // Retrieval info: PRIVATE: SWITCHOVER_FEATURE_ENABLED STRING "1"
 // Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
 // Retrieval info: PRIVATE: USE_CLK0 STRING "1"
+// Retrieval info: PRIVATE: USE_CLK1 STRING "1"
 // Retrieval info: PRIVATE: USE_CLKENA0 STRING "0"
+// Retrieval info: PRIVATE: USE_CLKENA1 STRING "0"
 // Retrieval info: PRIVATE: USE_MIL_SPEED_GRADE NUMERIC "0"
 // Retrieval info: PRIVATE: ZERO_DELAY_RADIO STRING "0"
 // Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
@@ -250,6 +272,10 @@ endmodule
 // Retrieval info: CONSTANT: CLK0_DUTY_CYCLE NUMERIC "50"
 // Retrieval info: CONSTANT: CLK0_MULTIPLY_BY NUMERIC "21"
 // Retrieval info: CONSTANT: CLK0_PHASE_SHIFT STRING "0"
+// Retrieval info: CONSTANT: CLK1_DIVIDE_BY NUMERIC "4"
+// Retrieval info: CONSTANT: CLK1_DUTY_CYCLE NUMERIC "50"
+// Retrieval info: CONSTANT: CLK1_MULTIPLY_BY NUMERIC "3"
+// Retrieval info: CONSTANT: CLK1_PHASE_SHIFT STRING "0"
 // Retrieval info: CONSTANT: INCLK0_INPUT_FREQUENCY NUMERIC "62500"
 // Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
 // Retrieval info: CONSTANT: LPM_TYPE STRING "altpll"
@@ -281,7 +307,7 @@ endmodule
 // Retrieval info: CONSTANT: PORT_SCANREAD STRING "PORT_UNUSED"
 // Retrieval info: CONSTANT: PORT_SCANWRITE STRING "PORT_UNUSED"
 // Retrieval info: CONSTANT: PORT_clk0 STRING "PORT_USED"
-// Retrieval info: CONSTANT: PORT_clk1 STRING "PORT_UNUSED"
+// Retrieval info: CONSTANT: PORT_clk1 STRING "PORT_USED"
 // Retrieval info: CONSTANT: PORT_clk2 STRING "PORT_UNUSED"
 // Retrieval info: CONSTANT: PORT_clk3 STRING "PORT_UNUSED"
 // Retrieval info: CONSTANT: PORT_clk4 STRING "PORT_UNUSED"
@@ -301,12 +327,14 @@ endmodule
 // Retrieval info: USED_PORT: @clk 0 0 5 0 OUTPUT_CLK_EXT VCC "@clk[4..0]"
 // Retrieval info: USED_PORT: areset 0 0 0 0 INPUT GND "areset"
 // Retrieval info: USED_PORT: c0 0 0 0 0 OUTPUT_CLK_EXT VCC "c0"
+// Retrieval info: USED_PORT: c1 0 0 0 0 OUTPUT_CLK_EXT VCC "c1"
 // Retrieval info: USED_PORT: inclk0 0 0 0 0 INPUT_CLK_EXT GND "inclk0"
 // Retrieval info: USED_PORT: locked 0 0 0 0 OUTPUT GND "locked"
 // Retrieval info: CONNECT: @areset 0 0 0 0 areset 0 0 0 0
 // Retrieval info: CONNECT: @inclk 0 0 1 1 GND 0 0 0 0
 // Retrieval info: CONNECT: @inclk 0 0 1 0 inclk0 0 0 0 0
 // Retrieval info: CONNECT: c0 0 0 0 0 @clk 0 0 1 0
+// Retrieval info: CONNECT: c1 0 0 0 0 @clk 0 0 1 1
 // Retrieval info: CONNECT: locked 0 0 0 0 @locked 0 0 0 0
 // Retrieval info: GEN_FILE: TYPE_NORMAL pll2_16.v TRUE
 // Retrieval info: GEN_FILE: TYPE_NORMAL pll2_16.ppf TRUE

+ 37 - 9
fpga/ip/pll2_48.v

@@ -41,11 +41,13 @@ module pll2_48 (
 	areset,
 	inclk0,
 	c0,
+	c1,
 	locked);
 
 	input	  areset;
 	input	  inclk0;
 	output	  c0;
+	output	  c1;
 	output	  locked;
 `ifndef ALTERA_RESERVED_QIS
 // synopsys translate_off
@@ -56,19 +58,21 @@ module pll2_48 (
 `endif
 
 	wire [4:0] sub_wire0;
-	wire  sub_wire2;
-	wire [0:0] sub_wire5 = 1'h0;
+	wire  sub_wire3;
+	wire [0:0] sub_wire6 = 1'h0;
+	wire [1:1] sub_wire2 = sub_wire0[1:1];
 	wire [0:0] sub_wire1 = sub_wire0[0:0];
 	wire  c0 = sub_wire1;
-	wire  locked = sub_wire2;
-	wire  sub_wire3 = inclk0;
-	wire [1:0] sub_wire4 = {sub_wire5, sub_wire3};
+	wire  c1 = sub_wire2;
+	wire  locked = sub_wire3;
+	wire  sub_wire4 = inclk0;
+	wire [1:0] sub_wire5 = {sub_wire6, sub_wire4};
 
 	altpll	altpll_component (
 				.areset (areset),
-				.inclk (sub_wire4),
+				.inclk (sub_wire5),
 				.clk (sub_wire0),
-				.locked (sub_wire2),
+				.locked (sub_wire3),
 				.activeclock (),
 				.clkbad (),
 				.clkena ({6{1'b1}}),
@@ -108,6 +112,10 @@ module pll2_48 (
 		altpll_component.clk0_duty_cycle = 50,
 		altpll_component.clk0_multiply_by = 7,
 		altpll_component.clk0_phase_shift = "0",
+		altpll_component.clk1_divide_by = 4,
+		altpll_component.clk1_duty_cycle = 50,
+		altpll_component.clk1_multiply_by = 1,
+		altpll_component.clk1_phase_shift = "0",
 		altpll_component.inclk0_input_frequency = 20833,
 		altpll_component.intended_device_family = "Cyclone IV E",
 		altpll_component.lpm_hint = "CBX_MODULE_PREFIX=pll2_48",
@@ -140,7 +148,7 @@ module pll2_48 (
 		altpll_component.port_scanread = "PORT_UNUSED",
 		altpll_component.port_scanwrite = "PORT_UNUSED",
 		altpll_component.port_clk0 = "PORT_USED",
-		altpll_component.port_clk1 = "PORT_UNUSED",
+		altpll_component.port_clk1 = "PORT_USED",
 		altpll_component.port_clk2 = "PORT_UNUSED",
 		altpll_component.port_clk3 = "PORT_UNUSED",
 		altpll_component.port_clk4 = "PORT_UNUSED",
@@ -181,8 +189,11 @@ endmodule
 // Retrieval info: PRIVATE: CUR_FBIN_CLK STRING "c0"
 // Retrieval info: PRIVATE: DEVICE_SPEED_GRADE STRING "Any"
 // Retrieval info: PRIVATE: DIV_FACTOR0 NUMERIC "1"
+// Retrieval info: PRIVATE: DIV_FACTOR1 NUMERIC "4"
 // Retrieval info: PRIVATE: DUTY_CYCLE0 STRING "50.00000000"
+// Retrieval info: PRIVATE: DUTY_CYCLE1 STRING "50.00000000"
 // Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE0 STRING "336.000000"
+// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE1 STRING "12.000000"
 // Retrieval info: PRIVATE: EXPLICIT_SWITCHOVER_COUNTER STRING "0"
 // Retrieval info: PRIVATE: EXT_FEEDBACK_RADIO STRING "0"
 // Retrieval info: PRIVATE: GLOCKED_COUNTER_EDIT_CHANGED STRING "1"
@@ -203,18 +214,26 @@ endmodule
 // Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE STRING "Not Available"
 // Retrieval info: PRIVATE: LVDS_MODE_DATA_RATE_DIRTY NUMERIC "0"
 // Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT0 STRING "deg"
+// Retrieval info: PRIVATE: LVDS_PHASE_SHIFT_UNIT1 STRING "ps"
 // Retrieval info: PRIVATE: MIG_DEVICE_SPEED_GRADE STRING "Any"
 // Retrieval info: PRIVATE: MIRROR_CLK0 STRING "0"
+// Retrieval info: PRIVATE: MIRROR_CLK1 STRING "0"
 // Retrieval info: PRIVATE: MULT_FACTOR0 NUMERIC "7"
+// Retrieval info: PRIVATE: MULT_FACTOR1 NUMERIC "1"
 // Retrieval info: PRIVATE: NORMAL_MODE_RADIO STRING "0"
 // Retrieval info: PRIVATE: OUTPUT_FREQ0 STRING "336.00000000"
+// Retrieval info: PRIVATE: OUTPUT_FREQ1 STRING "12.00000000"
 // Retrieval info: PRIVATE: OUTPUT_FREQ_MODE0 STRING "0"
+// Retrieval info: PRIVATE: OUTPUT_FREQ_MODE1 STRING "0"
 // Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT0 STRING "MHz"
+// Retrieval info: PRIVATE: OUTPUT_FREQ_UNIT1 STRING "MHz"
 // Retrieval info: PRIVATE: PHASE_RECONFIG_FEATURE_ENABLED STRING "1"
 // Retrieval info: PRIVATE: PHASE_RECONFIG_INPUTS_CHECK STRING "0"
 // Retrieval info: PRIVATE: PHASE_SHIFT0 STRING "0.00000000"
+// Retrieval info: PRIVATE: PHASE_SHIFT1 STRING "0.00000000"
 // Retrieval info: PRIVATE: PHASE_SHIFT_STEP_ENABLED_CHECK STRING "0"
 // Retrieval info: PRIVATE: PHASE_SHIFT_UNIT0 STRING "deg"
+// Retrieval info: PRIVATE: PHASE_SHIFT_UNIT1 STRING "ps"
 // Retrieval info: PRIVATE: PLL_ADVANCED_PARAM_CHECK STRING "0"
 // Retrieval info: PRIVATE: PLL_ARESET_CHECK STRING "1"
 // Retrieval info: PRIVATE: PLL_AUTOPLL_CHECK NUMERIC "1"
@@ -237,11 +256,14 @@ endmodule
 // Retrieval info: PRIVATE: SPREAD_USE STRING "0"
 // Retrieval info: PRIVATE: SRC_SYNCH_COMP_RADIO STRING "0"
 // Retrieval info: PRIVATE: STICKY_CLK0 STRING "1"
+// Retrieval info: PRIVATE: STICKY_CLK1 STRING "1"
 // Retrieval info: PRIVATE: SWITCHOVER_COUNT_EDIT NUMERIC "1"
 // Retrieval info: PRIVATE: SWITCHOVER_FEATURE_ENABLED STRING "1"
 // Retrieval info: PRIVATE: SYNTH_WRAPPER_GEN_POSTFIX STRING "0"
 // Retrieval info: PRIVATE: USE_CLK0 STRING "1"
+// Retrieval info: PRIVATE: USE_CLK1 STRING "1"
 // Retrieval info: PRIVATE: USE_CLKENA0 STRING "0"
+// Retrieval info: PRIVATE: USE_CLKENA1 STRING "0"
 // Retrieval info: PRIVATE: USE_MIL_SPEED_GRADE NUMERIC "0"
 // Retrieval info: PRIVATE: ZERO_DELAY_RADIO STRING "0"
 // Retrieval info: LIBRARY: altera_mf altera_mf.altera_mf_components.all
@@ -250,6 +272,10 @@ endmodule
 // Retrieval info: CONSTANT: CLK0_DUTY_CYCLE NUMERIC "50"
 // Retrieval info: CONSTANT: CLK0_MULTIPLY_BY NUMERIC "7"
 // Retrieval info: CONSTANT: CLK0_PHASE_SHIFT STRING "0"
+// Retrieval info: CONSTANT: CLK1_DIVIDE_BY NUMERIC "4"
+// Retrieval info: CONSTANT: CLK1_DUTY_CYCLE NUMERIC "50"
+// Retrieval info: CONSTANT: CLK1_MULTIPLY_BY NUMERIC "1"
+// Retrieval info: CONSTANT: CLK1_PHASE_SHIFT STRING "0"
 // Retrieval info: CONSTANT: INCLK0_INPUT_FREQUENCY NUMERIC "20833"
 // Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
 // Retrieval info: CONSTANT: LPM_TYPE STRING "altpll"
@@ -281,7 +307,7 @@ endmodule
 // Retrieval info: CONSTANT: PORT_SCANREAD STRING "PORT_UNUSED"
 // Retrieval info: CONSTANT: PORT_SCANWRITE STRING "PORT_UNUSED"
 // Retrieval info: CONSTANT: PORT_clk0 STRING "PORT_USED"
-// Retrieval info: CONSTANT: PORT_clk1 STRING "PORT_UNUSED"
+// Retrieval info: CONSTANT: PORT_clk1 STRING "PORT_USED"
 // Retrieval info: CONSTANT: PORT_clk2 STRING "PORT_UNUSED"
 // Retrieval info: CONSTANT: PORT_clk3 STRING "PORT_UNUSED"
 // Retrieval info: CONSTANT: PORT_clk4 STRING "PORT_UNUSED"
@@ -301,12 +327,14 @@ endmodule
 // Retrieval info: USED_PORT: @clk 0 0 5 0 OUTPUT_CLK_EXT VCC "@clk[4..0]"
 // Retrieval info: USED_PORT: areset 0 0 0 0 INPUT GND "areset"
 // Retrieval info: USED_PORT: c0 0 0 0 0 OUTPUT_CLK_EXT VCC "c0"
+// Retrieval info: USED_PORT: c1 0 0 0 0 OUTPUT_CLK_EXT VCC "c1"
 // Retrieval info: USED_PORT: inclk0 0 0 0 0 INPUT_CLK_EXT GND "inclk0"
 // Retrieval info: USED_PORT: locked 0 0 0 0 OUTPUT GND "locked"
 // Retrieval info: CONNECT: @areset 0 0 0 0 areset 0 0 0 0
 // Retrieval info: CONNECT: @inclk 0 0 1 1 GND 0 0 0 0
 // Retrieval info: CONNECT: @inclk 0 0 1 0 inclk0 0 0 0 0
 // Retrieval info: CONNECT: c0 0 0 0 0 @clk 0 0 1 0
+// Retrieval info: CONNECT: c1 0 0 0 0 @clk 0 0 1 1
 // Retrieval info: CONNECT: locked 0 0 0 0 @locked 0 0 0 0
 // Retrieval info: GEN_FILE: TYPE_NORMAL pll2_48.v TRUE
 // Retrieval info: GEN_FILE: TYPE_NORMAL pll2_48.ppf TRUE

+ 1 - 1
fpga/jic.cof.xml

@@ -16,7 +16,7 @@
 	<hex_block>
 		<hex_filename>../rv32/dram.hex</hex_filename>
 		<hex_addressing>relative</hex_addressing>
-		<hex_offset>2097152</hex_offset>
+		<hex_offset>1048576</hex_offset>
 		<hex_little_endian>0</hex_little_endian>
 	</hex_block>
 	<version>10</version>

+ 3 - 3
fpga/max80.qpf

@@ -19,14 +19,14 @@
 #
 # Quartus Prime
 # Version 21.1.0 Build 842 10/21/2021 SJ Lite Edition
-# Date created = 23:11:40  February 13, 2022
+# Date created = 04:03:01  February 18, 2022
 #
 # -------------------------------------------------------------------------- #
 
 QUARTUS_VERSION = "21.1"
-DATE = "23:11:40  February 13, 2022"
+DATE = "04:03:01  February 18, 2022"
 
 # Revisions
 
-PROJECT_REVISION = "v1"
 PROJECT_REVISION = "v2"
+PROJECT_REVISION = "v1"

+ 2 - 0
fpga/max80.qsf

@@ -80,6 +80,7 @@ set_global_assignment -name ENABLE_CONFIGURATION_PINS OFF
 set_global_assignment -name ENABLE_BOOT_SEL_PIN OFF
 set_global_assignment -name USE_CONFIGURATION_DEVICE ON
 set_global_assignment -name INTERNAL_FLASH_UPDATE_MODE "SINGLE COMP IMAGE"
+set_global_assignment -name STRATIXIII_UPDATE_MODE REMOTE
 set_global_assignment -name CRC_ERROR_OPEN_DRAIN OFF
 set_global_assignment -name GENERATE_JBC_FILE ON
 set_global_assignment -name STRATIX_DEVICE_IO_STANDARD "3.3-V LVTTL"
@@ -246,5 +247,6 @@ 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_global_assignment -name VERILOG_FILE ip/altera_remote_update_core.v
 
 set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

+ 100 - 87
fpga/max80.sv

@@ -13,123 +13,124 @@ module max80
     parameter logic [7:0] fpga_ver)
    (
     // Clock oscillator
-    input	  master_clk, // 336 MHz from PLL2
-    input	  master_pll_locked, // PLL2 is locked, master_clk is good
-    output	  reset_plls, // Reset all PLLs including PLL2
+    input 	  master_clk,	// 336 MHz from PLL2
+    input 	  slow_clk,	// ~12 MHz clock from PLL2
+    input 	  master_pll_locked, // PLL2 is locked, master_clk is good
+    output 	  reset_plls,	// Reset all PLLs including PLL2
 
-    input	  board_id, // This better match the firmware
+    input 	  board_id, // This better match the firmware
 
     // ABC-bus
-    inout	  abc_clk, // ABC-bus 3 MHz clock
+    inout 	  abc_clk, // ABC-bus 3 MHz clock
     inout [15:0]  abc_a, // ABC address bus
     inout [7:0]   abc_d, // ABC data bus
-    output	  abc_d_oe, // Data bus output enable
-    inout	  abc_rst_n, // ABC bus reset strobe
-    inout	  abc_cs_n, // ABC card select strobe
+    output 	  abc_d_oe, // Data bus output enable
+    inout 	  abc_rst_n, // ABC bus reset strobe
+    inout 	  abc_cs_n, // ABC card select strobe
     inout [4:0]   abc_out_n, // OUT, C1-C4 strobe
     inout [1:0]   abc_inp_n, // INP, STATUS strobe
-    inout	  abc_xmemfl_n, // Memory read strobe
-    inout	  abc_xmemw800_n, // Memory write strobe (ABC800)
-    inout	  abc_xmemw80_n, // Memory write strobe (ABC80)
-    inout	  abc_xinpstb_n, // I/O read strobe (ABC800)
-    inout	  abc_xoutpstb_n, // I/O write strobe (ABC80)
+    inout 	  abc_xmemfl_n, // Memory read strobe
+    inout 	  abc_xmemw800_n, // Memory write strobe (ABC800)
+    inout 	  abc_xmemw80_n, // Memory write strobe (ABC80)
+    inout 	  abc_xinpstb_n, // I/O read strobe (ABC800)
+    inout 	  abc_xoutpstb_n, // I/O write strobe (ABC80)
     // The following are inverted versus the bus IF
     // the corresponding MOSFETs are installed
-    inout	  abc_rdy_x, // RDY = WAIT#
-    inout	  abc_resin_x, // System reset request
-    inout	  abc_int80_x, // System INT request (ABC80)
-    inout	  abc_int800_x, // System INT request (ABC800)
-    inout	  abc_nmi_x, // System NMI request (ABC800)
-    inout	  abc_xm_x, // System memory override (ABC800)
+    inout 	  abc_rdy_x, // RDY = WAIT#
+    inout 	  abc_resin_x, // System reset request
+    inout 	  abc_int80_x, // System INT request (ABC80)
+    inout 	  abc_int800_x, // System INT request (ABC800)
+    inout 	  abc_nmi_x, // System NMI request (ABC800)
+    inout 	  abc_xm_x, // System memory override (ABC800)
     // Host/device control
-    output	  abc_host, // 1 = host, 0 = target
+    output 	  abc_host, // 1 = host, 0 = target
 
     // ABC-bus extension header
     // (Note: cannot use an array here because HC and HH are
     // input only.)
-    inout	  exth_ha,
-    inout	  exth_hb,
-    input	  exth_hc,
-    inout	  exth_hd,
-    inout	  exth_he,
-    inout	  exth_hf,
-    inout	  exth_hg,
-    input	  exth_hh,
+    inout 	  exth_ha,
+    inout 	  exth_hb,
+    input 	  exth_hc,
+    inout 	  exth_hd,
+    inout 	  exth_he,
+    inout 	  exth_hf,
+    inout 	  exth_hg,
+    input 	  exth_hh,
 
     // SDRAM bus
-    output	  sr_clk,
+    output 	  sr_clk,
     output [1:0]  sr_ba, // Bank address
     output [12:0] sr_a, // Address within bank
     inout [15:0]  sr_dq, // Also known as D or IO
     output [1:0]  sr_dqm, // DQML and DQMH
-    output	  sr_cs_n,
-    output	  sr_we_n,
-    output	  sr_cas_n,
-    output	  sr_ras_n,
+    output 	  sr_cs_n,
+    output 	  sr_we_n,
+    output 	  sr_cas_n,
+    output 	  sr_ras_n,
 
     // SD card
-    input	  sd_cd_n,
-    output	  sd_cs_n,
-    output	  sd_clk,
-    output	  sd_di,
-    input	  sd_do,
+    input 	  sd_cd_n,
+    output 	  sd_cs_n,
+    output 	  sd_clk,
+    output 	  sd_di,
+    input 	  sd_do,
 
     // Serial console (naming is FPGA as DCE)
-    input	  tty_txd,
-    output	  tty_rxd,
-    input	  tty_rts,
-    output	  tty_cts,
-    input	  tty_dtr,
+    input 	  tty_txd,
+    output 	  tty_rxd,
+    input 	  tty_rts,
+    output 	  tty_cts,
+    input 	  tty_dtr,
 
     // SPI flash memory (also configuration)
-    output	  flash_cs_n,
-    output	  flash_sck,
+    output 	  flash_cs_n,
+    output 	  flash_sck,
     inout [1:0]   flash_io,
 
     // SPI bus (connected to ESP32 so can be bidirectional)
-    inout	  spi_clk,
-    inout	  spi_miso,
-    inout	  spi_mosi,
-    inout	  spi_cs_esp_n, // ESP32 IO10
-    inout	  spi_cs_flash_n, // ESP32 IO01
+    inout 	  spi_clk,
+    inout 	  spi_miso,
+    inout 	  spi_mosi,
+    inout 	  spi_cs_esp_n, // ESP32 IO10
+    inout 	  spi_cs_flash_n, // ESP32 IO01
 
     // Other ESP32 connections
-    inout	  esp_io0, // ESP32 IO00
-    inout	  esp_int, // ESP32 IO09
+    inout 	  esp_io0, // ESP32 IO00
+    inout 	  esp_int, // ESP32 IO09
 
     // I2C bus (RTC and external)
-    inout	  i2c_scl,
-    inout	  i2c_sda,
-    input	  rtc_32khz,
-    input	  rtc_int_n,
+    inout 	  i2c_scl,
+    inout 	  i2c_sda,
+    input 	  rtc_32khz,
+    input 	  rtc_int_n,
 
     // LEDs
     output [2:0]  led,
 
     // USB
-    inout	  usb_dp,
-    inout	  usb_dn,
-    output	  usb_pu,
-    input	  usb_rx,
-    input	  usb_rx_ok,
+    inout 	  usb_dp,
+    inout 	  usb_dn,
+    output 	  usb_pu,
+    input 	  usb_rx,
+    input 	  usb_rx_ok,
 
     // HDMI
     output [2:0]  hdmi_d,
-    output	  hdmi_clk,
-    inout	  hdmi_scl,
-    inout	  hdmi_sda,
-    inout	  hdmi_hpd,
+    output 	  hdmi_clk,
+    inout 	  hdmi_scl,
+    inout 	  hdmi_sda,
+    inout 	  hdmi_hpd,
 
     // Unconnected pins with pullups, used for randomness
     inout [2:0]   rngio,
 
     // Various clocks available to the top level as well as internally
-    output	  sdram_clk,	// 168 MHz SDRAM clock
-    output	  sys_clk,	//  84 MHz System clock
-    output	  flash_clk,	// 134 MHz Serial flash ROM clock
-    output	  usb_clk,	//  48 MHz USB clock
-    output	  vid_clk,	//  56 MHz Video pixel clock
-    output	  vid_hdmiclk	// 280 MHz HDMI serializer clock = vid_clk x 5
+    output 	  sdram_clk, // 168 MHz SDRAM clock
+    output 	  sys_clk, //  84 MHz System clock
+    output 	  flash_clk, // 134 MHz Serial flash ROM clock
+    output 	  usb_clk, //  48 MHz USB clock
+    output 	  vid_clk, //  56 MHz Video pixel clock
+    output 	  vid_hdmiclk	// 280 MHz HDMI serializer clock = vid_clk x 5
     );
 
    // -----------------------------------------------------------------------
@@ -137,9 +138,7 @@ module max80
    // -----------------------------------------------------------------------
    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) );
+   wire 		    reconfig_rst;	 // Reconfigure FPGA
 
    assign reset_plls = 1'b0;
    
@@ -147,6 +146,12 @@ module max80
 
    assign   pll_locked[2] = master_pll_locked;
 
+   fpgarst fpgarst (
+		    .rst_n    ( master_pll_locked ),
+		    .clk      ( slow_clk ),
+		    .reconfig ( reconfig_rst )
+		    );
+
    //
    // Clocks.
    //
@@ -213,12 +218,14 @@ module max80
    wire [3:1] aux_reset_cmd;	// Other reset sources
    reg [3:1]  reset_cmd_q = 3'b0;
 
+   assign reconfig_rst = reset_cmd_q[3];
+   
    always @(negedge all_plls_locked or posedge sys_clk)
      if (~all_plls_locked)
        begin
 	  hard_rst_n         <= 1'b0;
 	  rst_n              <= 1'b0;
-	  reset_cmd_q        <= 3'b0;
+	  reset_cmd_q[2:1]   <= 3'b0;
 	  cpu_reset_cmd_q[0] <= 3'b0;
 	  cpu_reset_cmd_q[1] <= 3'b0;
 	  sys_clk_ctr        <= (-'sb1) << reset_pow2;
@@ -233,8 +240,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
+	  // Reconfig reset is sticky until FPGA reloaded...
+	  reset_cmd_q[3] <= reset_cmd_q[3] | cpu_reset_cmd_q[0][3] |
+			    aux_reset_cmd[3];
 
 	  if (|reset_cmd_q)
 	    begin
@@ -464,37 +472,42 @@ module max80
    // Edge-triggered IRQs. picorv32 latches interrupts
    // but doesn't edge detect for a slow signal, so do it
    // here instead and use level triggered signalling to the
-   // CPU.
-   wire [31:0] cpu_eoi;
-   reg  [31:0] cpu_eoi_q;
+   // CPU. This also allows using an explicit EOI instead of
+   // using EOI-on-INTACK.
+   //
 
    // sys_irq defined in iodevs.vh
    reg  [31:0] sys_irq_q;
    reg  [31:0] cpu_irq;
 
+   wire [31:0] cpu_eoi = {32{sysreg[4]}}
+	       & {{8{cpu_mem_wstrb[3]}}, {8{cpu_mem_wstrb[2]}},
+		  {8{cpu_mem_wstrb[1]}}, {8{cpu_mem_wstrb[0]}}}
+	       & cpu_mem_wdata;
+
+   // Reading the register shows the current set of pending interrupts.
+   assign sysreg_rdata[4] = cpu_irq;
+   
    // CPU permanently hung?
    wire	       cpu_trap;
 
    // Request to halt the CPU on the next instruction boundary
    wire        cpu_halt;
-
+   
    always @(negedge rst_n or posedge sys_clk)
      if (~rst_n)
        begin
 	  sys_irq_q <= 32'b0;
-	  cpu_eoi_q <= 32'b0;
 	  cpu_irq   <= 32'b0;
        end
      else
        begin
 	  sys_irq_q <= sys_irq & irq_edge_mask;
-	  cpu_eoi_q <= cpu_eoi & irq_edge_mask;
 
-	  cpu_irq <= (sys_irq & ~sys_irq_q)
-	    | (cpu_irq & irq_edge_mask & ~(cpu_eoi & ~cpu_eoi_q));
+	  cpu_irq <= (sys_irq & ~sys_irq_q) |
+		     (cpu_irq & irq_edge_mask & ~cpu_eoi);
        end
 
-
    picorv32 #(
 	      .ENABLE_COUNTERS ( 1 ),
 	      .ENABLE_COUNTERS64 ( 1 ),
@@ -540,7 +553,7 @@ module max80
 	.mem_la_wstrb ( cpu_la_wstrb ),
 
 	.irq ( cpu_irq ),
-	.eoi ( cpu_eoi )
+	.eoi ( )
 	);
 
    // Add a mandatory wait state to iodevs to reduce the size

BIN
fpga/output/jtagupd/v1.rbf.gz


BIN
fpga/output/jtagupd/v1.sof


BIN
fpga/output/jtagupd/v1.svf.gz


BIN
fpga/output/jtagupd/v2.rbf.gz


BIN
fpga/output/jtagupd/v2.sof


BIN
fpga/output/jtagupd/v2.svf.gz


BIN
fpga/output/v1.fw


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.update.svf.gz


BIN
fpga/output/v1.update.xsvf.gz


BIN
fpga/output/v1.xsvf.gz


BIN
fpga/output/v2.fw


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.update.svf.gz


BIN
fpga/output/v2.update.xsvf.gz


BIN
fpga/output/v2.xsvf.gz


+ 1 - 1
fpga/pof.cof.xml

@@ -15,7 +15,7 @@
 	<hex_block>
 		<hex_filename>../rv32/dram.hex</hex_filename>
 		<hex_addressing>relative</hex_addressing>
-		<hex_offset>2097152</hex_offset>
+		<hex_offset>1048576</hex_offset>
 		<hex_little_endian>0</hex_little_endian>
 	</hex_block>
 	<version>10</version>

+ 8 - 1
fpga/scripts/getvir.pl → fpga/scripts/flashsvf.pl

@@ -12,7 +12,7 @@ my $flashcmd   = 0xabc80046;
 my $flashpfx   = 0xabc80fed;
 my $maxdatalen = 16 << 20;
 
-my($rptfile, $binfile, $svffile) = @ARGV;
+my($svfload, $rptfile, $binfile, $svffile) = @ARGV;
 
 open(my $in, '<', $rptfile)
     or die "$0: $rptfile: $!\n";
@@ -52,6 +52,13 @@ close($bin);
 open(my $svf, '>', $svffile)
         or die "$0: $svffile: $!\n";
 
+# SVF file for the transient load image
+open(my $in, '<', $svfload)
+    or die "$0: $svfload: $!\n";
+while (defined(my $l = <$in>)) {
+    print $svf $l;
+}
+close($in);
 
 sub print_bitstring($$$) {
     my($svf, $len, $data) = @_;

+ 50 - 47
fpga/spirom.sv

@@ -242,12 +242,11 @@ module spirom (
    reg	       spi_in_req;
    reg	       spi_in_req_q;
 
-   // Wait these many bit times between low CS# and the first data
-   // clock (tSHCH), and between CS# high and the next CS# high
+   // Wait these many bit times between CS# high and the next CS# high
    // (tSHSL). The worst of these is tSHSL2 = 50 ns = 8 cycles @ 134 MHz.
    localparam spi_cs_wait_lg2 = 3;
-   reg [spi_cs_wait_lg2:0]   spi_cs_ctr;
-   wire			     spi_cs_ready = spi_cs_ctr[spi_cs_wait_lg2];
+   reg [spi_cs_wait_lg2:0] spi_cs_ctr;
+   wire 		   spi_cs_ready = spi_cs_ctr[spi_cs_wait_lg2];
 
    // Explicit synchronizers for handshake signals
    synchronizer #(.width(1)) go_spi_synchro
@@ -271,21 +270,21 @@ module spirom (
    always @(negedge rst_n or posedge rom_clk)
      if (~rst_n)
        begin
-	  spi_cmd_ctr    <= 6'b0;
-	  spi_clk_en     <= 1'b0;
-	  spi_clk_en_q   <= 'b0;
-	  spi_data_ctr   <= 27'b0;
-	  spi_cs_n       <= 1'b1;
-	  spi_cs_ctr     <= 'b0;
-	  spi_in_req     <= 1'b0;
-	  spi_in_req_q   <= 1'b0;
+	  spi_cmd_ctr  <= 6'b0;
+	  spi_clk_en   <= 1'b0;
+	  spi_clk_en_q <= 'b0;
+	  spi_data_ctr <= 27'b0;
+	  spi_cs_n     <= 1'b1;
+	  spi_cs_ctr   <= 'b0;
+	  spi_in_req   <= 1'b0;
+	  spi_in_req_q <= 1'b0;
 	  spi_ram_in_req <= 1'b0;
-	  spi_mosi_en    <= 1'b1;
-	  spi_in_shr     <= 32'b0;
-	  spi_active     <= 1'b0;
-	  spi_active_q   <= 1'b0;
-	  spi_more_q     <= 1'b0;
-	  spi_out_shr    <= 32'b0;
+	  spi_mosi_en  <= 1'b1;
+	  spi_in_shr   <= 32'b0;
+	  spi_active   <= 1'b0;
+	  spi_active_q <= 1'b0;
+	  spi_more_q   <= 1'b0;
+	  spi_out_shr  <= 32'b0;
        end
      else
        begin
@@ -309,38 +308,42 @@ module spirom (
 	  if ( spi_clk_en_q )
 	    spi_out_shr <= { spi_out_shr[30:0], 1'b1 };
 
-	  // CS# delay counter (see above)
-	  spi_cs_ctr <= spi_cs_ctr + !spi_cs_ready;
+	  // tSHSL: make sure we get 8 bit times of CS# deselect between
+	  // commands.
+	  if ( ~spi_cs_n )
+	    spi_cs_ctr <= 'b0;
+	  else
+	    spi_cs_ctr <= spi_cs_ctr + !spi_cs_ready;
 
 	  // Note: datalen <- spi_data_ctr is a 2-cycle multipath
-	  if (go_spi_s & ~spi_active & spi_cs_ready)
+	  if (~spi_active)
 	    begin
-	       // Starting new transaction
-	       spi_cmd_ctr  <= { cmdlen,  3'b0 };
-	       spi_data_ctr <= { datalen, 5'b0 };
-	       spi_active   <= 1'b1;
-	       spi_cs_n     <= 1'b0;
-	       spi_more_q   <= spi_more;
-	       spi_out_shr  <= romcmd;
-	    end
-	  else if ( ~|{spi_data_ctr, spi_cmd_ctr} )
-	    begin
-	       // Transaction completed
-	       spi_clk_en  <= 1'b0;
-	       spi_mosi_en <= 1'b1;
-	       spi_cs_n    <= ~spi_more_q;
-	       spi_active  <= 1'b0;
-	       if (~spi_more_q)
-		 spi_cs_ctr <= 'b0;
-	    end
+	       spi_cs_n <= ~spi_more_q;
+
+	       if ( go_spi_s & (spi_more_q | spi_cs_ready) )
+		 begin
+		    // Starting new transaction
+		    spi_cmd_ctr  <= { cmdlen,  3'b0 };
+		    spi_data_ctr <= { datalen, 5'b0 };
+		    spi_active   <= 1'b1;
+		    spi_cs_n     <= 1'b0;
+		    spi_more_q   <= spi_more;
+		    spi_out_shr  <= romcmd;
+		 end
+	    end // if (~spi_active)
 	  else
 	    begin
-	       spi_active   <= 1'b1;
-	       spi_cs_n     <= 1'b0;
-	       if ( ~spi_active )
-		 spi_cs_ctr <= 'b0;
+	       spi_cs_n <= 1'b0;
 
-	       if ( spi_active & spi_cs_ready )
+	       if ( ~|{spi_data_ctr, spi_cmd_ctr} )
+		 begin
+		    // Transaction completed. Note: CS# needs to remain
+		    // asserted for at least one more cycle in case of read.
+		    spi_clk_en  <= 1'b0;
+		    spi_mosi_en <= 1'b1;
+		    spi_active  <= 1'b0;
+		 end
+	       else
 		 begin
 		    // This will block unnecessarily if the DMA queue
 		    // is full from a previous transaction, but that doesn't
@@ -351,7 +354,7 @@ module spirom (
 		    if ( spi_clk_en & ~|spi_cmd_ctr )
 		      spi_in_req <= spi_data_ctr[-3] | spi_dual;
 
-		    if (  ~spi_active_q | spi_clk_en )
+		    if ( spi_clk_en | ~spi_active_q )
 		      begin
 			 // This is either the kickoff cycle or advancing
 			 if ( ~|spi_cmd_ctr )
@@ -359,8 +362,8 @@ module spirom (
 			 else
 			   spi_cmd_ctr <= spi_cmd_ctr - 1'b1;
 		      end // if ( spi_clk_en )
-		 end // if ( spi_active & spi_cs_ready )
-	    end // else: !if( ~|{spi_data_ctr, spi_cmd_ctr} )
+		 end // else: !if( ~|{spi_data_ctr, spi_cmd_ctr} )
+	    end // else: !if(~spi_active)
        end // else: !if(~rst_n)
 
    //

+ 63 - 61
fpga/v1.sv

@@ -8,94 +8,94 @@
 module v1
    (
     // Clock oscillator
-    input	  clock_48, // 48 MHz
-    input	  board_id, // This better match the firmware
+    input 	  clock_48, // 48 MHz
+    input 	  board_id, // This better match the firmware
 
     // ABC-bus
-    input	  abc_clk, // ABC-bus 3 MHz clock
+    input 	  abc_clk, // ABC-bus 3 MHz clock
     input [15:0]  abc_a, // ABC address bus
     inout [7:0]   abc_d, // ABC data bus
-    output	  abc_d_oe, // Data bus output enable
-    input	  abc_rst_n, // ABC bus reset strobe
-    input	  abc_cs_n, // ABC card select strobe
+    output 	  abc_d_oe, // Data bus output enable
+    input 	  abc_rst_n, // ABC bus reset strobe
+    input 	  abc_cs_n, // ABC card select strobe
     input [4:0]   abc_out_n, // OUT, C1-C4 strobe
     input [1:0]   abc_inp_n, // INP, STATUS strobe
-    input	  abc_xmemfl_n, // Memory read strobe
-    input	  abc_xmemw800_n, // Memory write strobe (ABC800)
-    input	  abc_xmemw80_n, // Memory write strobe (ABC80)
-    input	  abc_xinpstb_n, // I/O read strobe (ABC800)
-    input	  abc_xoutpstb_n, // I/O write strobe (ABC80)
+    input 	  abc_xmemfl_n, // Memory read strobe
+    input 	  abc_xmemw800_n, // Memory write strobe (ABC800)
+    input 	  abc_xmemw80_n, // Memory write strobe (ABC80)
+    input 	  abc_xinpstb_n, // I/O read strobe (ABC800)
+    input 	  abc_xoutpstb_n, // I/O write strobe (ABC80)
     // The following are inverted versus the bus IF
     // the corresponding MOSFETs are installed
-    output	  abc_rdy_x, // RDY = WAIT#
-    output	  abc_resin_x, // System reset request
-    output	  abc_int80_x, // System INT request (ABC80)
-    output	  abc_int800_x, // System INT request (ABC800)
-    output	  abc_nmi_x, // System NMI request (ABC800)
-    output	  abc_xm_x, // System memory override (ABC800)
+    output 	  abc_rdy_x, // RDY = WAIT#
+    output 	  abc_resin_x, // System reset request
+    output 	  abc_int80_x, // System INT request (ABC80)
+    output 	  abc_int800_x, // System INT request (ABC800)
+    output 	  abc_nmi_x, // System NMI request (ABC800)
+    output 	  abc_xm_x, // System memory override (ABC800)
     // Host/device control
-    output	  abc_host, // 1 = host, 0 = target
-    output	  abc_a_oe,
+    output 	  abc_host, // 1 = host, 0 = target
+    output 	  abc_a_oe,
     // Bus isolation
-    output	  abc_d_ce_n,
+    output 	  abc_d_ce_n,
 
     // ABC-bus extension header
     // (Note: cannot use an array here because HC and HH are
     // input only.)
-    inout	  exth_ha,
-    inout	  exth_hb,
-    input	  exth_hc,
-    inout	  exth_hd,
-    inout	  exth_he,
-    inout	  exth_hf,
-    inout	  exth_hg,
-    input	  exth_hh,
+    inout 	  exth_ha,
+    inout 	  exth_hb,
+    input 	  exth_hc,
+    inout 	  exth_hd,
+    inout 	  exth_he,
+    inout 	  exth_hf,
+    inout 	  exth_hg,
+    input 	  exth_hh,
 
     // SDRAM bus
-    output	  sr_clk,
-    output	  sr_cke,
+    output 	  sr_clk,
+    output 	  sr_cke,
     output [1:0]  sr_ba, // Bank address
     output [12:0] sr_a, // Address within bank
     inout [15:0]  sr_dq, // Also known as D or IO
     output [1:0]  sr_dqm, // DQML and DQMH
-    output	  sr_cs_n,
-    output	  sr_we_n,
-    output	  sr_cas_n,
-    output	  sr_ras_n,
+    output 	  sr_cs_n,
+    output 	  sr_we_n,
+    output 	  sr_cas_n,
+    output 	  sr_ras_n,
 
     // SD card
-    output	  sd_clk,
-    output	  sd_cmd,
+    output 	  sd_clk,
+    output 	  sd_cmd,
     inout [3:0]   sd_dat,
 
     // Serial console (naming is FPGA as DCE)
-    input	  tty_txd,
-    output	  tty_rxd,
-    input	  tty_rts,
-    output	  tty_cts,
-    input	  tty_dtr,
+    input 	  tty_txd,
+    output 	  tty_rxd,
+    input 	  tty_rts,
+    output 	  tty_cts,
+    input 	  tty_dtr,
 
     // SPI flash memory (also configuration)
-    output	  flash_cs_n,
-    output	  flash_sck,
+    output 	  flash_cs_n,
+    output 	  flash_sck,
     inout [1:0]   flash_io,
 
     // SPI bus (connected to ESP32 so can be bidirectional)
-    inout	  spi_clk,
-    inout	  spi_miso,
-    inout	  spi_mosi,
-    inout	  spi_cs_esp_n, // ESP32 IO10
-    inout	  spi_cs_flash_n, // ESP32 IO01
+    inout 	  spi_clk,
+    inout 	  spi_miso,
+    inout 	  spi_mosi,
+    inout 	  spi_cs_esp_n, // ESP32 IO10
+    inout 	  spi_cs_flash_n, // ESP32 IO01
 
     // Other ESP32 connections
-    inout	  esp_io0, // ESP32 IO00
-    inout	  esp_int, // ESP32 IO09
+    inout 	  esp_io0, // ESP32 IO00
+    inout 	  esp_int, // ESP32 IO09
 
     // I2C bus (RTC and external)
-    inout	  i2c_scl,
-    inout	  i2c_sda,
-    input	  rtc_32khz,
-    input	  rtc_int_n,
+    inout 	  i2c_scl,
+    inout 	  i2c_sda,
+    input 	  rtc_32khz,
+    input 	  rtc_int_n,
 
     // LED (2 = D23/G, 1 = D22/R, 0 = D17/B)
     output [2:0]  led,
@@ -105,10 +105,10 @@ module v1
 
     // HDMI
     output [2:0]  hdmi_d,
-    output	  hdmi_clk,
-    inout	  hdmi_scl,
-    inout	  hdmi_sda,
-    inout	  hdmi_hpd,
+    output 	  hdmi_clk,
+    inout 	  hdmi_scl,
+    inout 	  hdmi_sda,
+    inout 	  hdmi_hpd,
 
     // Unconnected pins with pullups, used for randomness
     inout [2:0]   rngio
@@ -132,20 +132,22 @@ module v1
    // Master PLL: 48 -> 336 MHz
    wire		  reset_plls;
    wire		  master_pll_locked;
-   wire		  master_clk;
+   wire		  master_clk;	// 336 MHz
+   wire 	  slow_clk;	//  12 MHz
 
    pll2_48 pll2 (
 		 .areset ( reset_plls ),
 		 .locked ( master_pll_locked ),
 		 .inclk0 ( clock_48 ),
-		 .c0 ( master_clk )
+		 .c0 ( master_clk ),
+		 .c1 ( slow_clk )
 		 );
 
-
    max80 #(.x_mosfet(6'b111111),
 	   .fpga_ver(8'd1))
    max80 (
 	  .master_clk             ( master_clk ),
+	  .slow_clk               ( slow_clk ),
 	  .master_pll_locked	  ( master_pll_locked ),
 	  .reset_plls             ( reset_plls ),
 	  .board_id               ( board_id ),

+ 5 - 2
fpga/v2.vh

@@ -119,13 +119,15 @@ module `TOP
    // Master PLL: 16 -> 336 MHz
    wire		  reset_plls;
    wire		  master_pll_locked;
-   wire		  master_clk;
+   wire		  master_clk;	// 336 MHz
+   wire 	  slow_clk;	//  12 MHz
 
    pll2_16 pll2 (
 		 .areset ( reset_plls ),
 		 .locked ( master_pll_locked ),
 		 .inclk0 ( clock_16 ),
-		 .c0 ( master_clk )
+		 .c0 ( master_clk ),
+		 .c1 ( slow_clk )
 		 );
 
    wire		  usb_clk;
@@ -135,6 +137,7 @@ module `TOP
 	   .fpga_ver(8'd2))
    `MAIN (
 	  .master_clk             ( master_clk ),
+	  .slow_clk               ( slow_clk ),
 	  .master_pll_locked	  ( master_pll_locked ),
 	  .reset_plls             ( reset_plls ),
 	  .board_id               ( board_id ),

+ 6 - 4
fpga/vjtag_max80.sv

@@ -196,6 +196,7 @@ module vjtag_max80
    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] } :
@@ -211,7 +212,8 @@ module vjtag_max80
 
 	if ( tck_stb )
 	  begin
-	     if ( v_st_sdr )
+	     v_st_sdr_q <= v_st_sdr;
+	     if ( v_st_sdr_q )
 	       jtag_shr <= jtag_shr_in;
 
 	     if ( v_st_cdr )
@@ -344,13 +346,13 @@ module vjtag_max80
 	     if ( v_st_udr & ~ir_ro )
 	       case ( ir_cmd )
 		 cmd_halt: begin
-		    jtag_cpu_halt  <= jtag_shr_in[0];
+		    jtag_cpu_halt  <= jtag_shr[0];
 		 end
 		 cmd_memaddr: begin
-		    jtag_memaddr   <= maskaddr(jtag_shr_in);
+		    jtag_memaddr   <= maskaddr(jtag_shr);
 		 end
 		 cmd_cpucmd, cmd_cpucmd_irq: begin
-		    jtag_cpucmd    <= jtag_shr_in;
+		    jtag_cpucmd    <= jtag_shr;
 		 end
 		 default:     /* nothing */ ;
 	       endcase // case ( ir_cmd )

+ 1 - 1
rv32/Makefile

@@ -108,7 +108,7 @@ testimg.o: testimg.S testimg.bin
 
 %.elf: %.ild %.o $(LIBS)
 	$(CC) $(LDFLAGS) -Wl,-T,$< -o $@ \
-		-Wl,--start-group $(filter-out $<,$^)
+		-Wl,--start-group $(filter-out $<,$^) -Wl,--end-group
 
 %.o: %.c | $(genhdrs)
 	$(CC) $(CFLAGS) $(CFLAGS_$<) $(gendeps) -c -o $@ $<

+ 1 - 1
rv32/checksum.h

@@ -1,4 +1,4 @@
 #ifndef CHECKSUM_H
 #define CHECKSUM_H
-#define SDRAM_SUM 0xb5bad356
+#define SDRAM_SUM 0xa9babb0a
 #endif

+ 0 - 1
rv32/die.c

@@ -20,4 +20,3 @@ void _die(void)
 #pragma GCC diagnostic ignored "-Wattribute-alias"
 void _exit(int) __attribute__((alias("_die")));
 void exit(int) __attribute__((alias("_die")));
-void _irq(void) __attribute__((weak,alias("_die")));

+ 21 - 4
rv32/io.h

@@ -63,16 +63,22 @@ static __always_inline uint64_t rdtimeq(void)
     return ((uint64_t)(((int32_t)l < 0) ? h1 : h0) << 32) + l;
 }
 
-static __always_inline void udelay(uint32_t us)
+static __always_inline void cdelay(uint32_t cycles)
 {
-    if (!__builtin_constant_p(us) || us) {
-	uint32_t cycles = us * (CPU_HZ / 1000000);
+    if (!__builtin_constant_p(cycles) || cycles >= 4) {
 	uint32_t start = rdtime();
-
 	while (rdtime() - start < cycles)
 	    relax();
     }
 }
+static __always_inline void udelay(uint32_t us)
+{
+    cdelay(us * (CPU_HZ / 1000000));
+}
+static __always_inline void mdelay(uint32_t ms)
+{
+    cdelay(ms * (CPU_HZ / 1000));
+}
 
 static inline void i2c_set_speed(unsigned int khz)
 {
@@ -94,4 +100,15 @@ static inline uint32_t rdrand(void)
     return RANDOM_DATA;
 }
 
+/* Send EOI for some interrupt(s) */
+static inline void eoi_mask(uint32_t mask)
+{
+    SYS_EOI = mask;
+}
+
+static inline void eoi(unsigned int irq)
+{
+    SYS_EOI = 1 << irq;
+}
+
 #endif /* IO_H */

+ 1 - 0
rv32/ioregs.h

@@ -20,6 +20,7 @@
 #define SYS_RESET_SOFT		1
 #define SYS_RESET_HARD		2
 #define SYS_RESET_RECONFIG	3
+#define SYS_EOI			IODEVL(SYS,4)
 
 #define ROMCOPY_RAMADDR		IODEVL(ROMCOPY,0)
 #define ROMCOPY_ROMCMD		IODEVL(ROMCOPY,1)

+ 8 - 2
rv32/irqasm.S

@@ -1,6 +1,6 @@
 #include "compiler.h"
 #include "picorv32.h"
-#include "iodevs.h"
+#include "ioregs.h"
 
 
 	// The IRQ dispatch code is written in assembly to make
@@ -17,6 +17,9 @@ _irq:
 	// s10 contains the IRQ return address, s11 the mask of
 	// IRQs to be handled.
 
+	// Send EOI for all interrupts (previously done in hardware)
+	sw s11,SYS_EOI(zero)
+
 	// Fast dispatch for the ABC-bus interrupt handler
 	andi s0,s11,1 << ABC_IRQ
 	beqz s0,1f
@@ -80,7 +83,7 @@ _irq:
 
 	// Debug functions to read and write x-registers from interrupt
 	// mode by register number
-	__text_hot
+	.pushsection ".text.hot.rdxreg","ax"
 	.globl	rdxreg
 	.balign 4
 rdxreg:
@@ -93,7 +96,9 @@ rdxreg:
 	jr a3
 	.type rdxreg, @function
 	.size rdxreg, . - rdxreg
+	.popsection
 
+	.pushsection ".text.hot.wrxreg","ax"
 	.globl	wrxreg
 	.balign 4
 wrxreg:
@@ -106,6 +111,7 @@ wrxreg:
 	jr a3
 	.type wrxreg, @function
 	.size wrxreg, . - wrxreg
+	.popsection
 
 	__rwtext
 	.balign 4

+ 12 - 11
rv32/jtagupd.c

@@ -2,6 +2,7 @@
 #include "io.h"
 #include "sys.h"
 #include "console.h"
+#include "zlib.h"
 
 #define VJTAG_FLASH_CMD	0xabc80046
 
@@ -16,10 +17,6 @@ IRQHANDLER(tty,1)
 {
     irqhandler_spurious_0(vector, pc);
 }
-IRQHANDLER(romcopy,0)
-{
-    irqhandler_spurious_0(vector, pc);
-}
 
 /*
  * SDRAM upload buffer - always first in SDRAM
@@ -32,24 +29,28 @@ void main(void)
     static const char hello[] =
 	"\n\nMAX80 JTAG update firmware compiled on: ";
 
-    unmask_irqs((1U << EBREAK_IRQ)|(1U << BUSERR_IRQ));
-
-    romcopy_bzero(__dram_bss_start, __dram_bss_end - __dram_bss_start);
+    unmask_irqs((1U << ROMCOPY_IRQ)|(1U << EBREAK_IRQ)|(1U << BUSERR_IRQ));
 
     con_puts(hello);
     con_puts(__datestamp);
     con_putc('\n');
 
+    wait_romcopy_done();
+    rom_print_serial();
+
     while (1) {
 	uint32_t cmd;
 
 	waitfor(VJTAG_IRQ);
+	eoi(VJTAG_IRQ);
 	cmd = VJTAG_CPUCMD;
 
-	if (cmd != VJTAG_FLASH_CMD)
+	if (cmd == VJTAG_FLASH_CMD) {
+	    con_puts("JTAG: starting firmware flash process\n");
+	    rom_flash_from_memory(jtag_flash_buf, sizeof jtag_flash_buf);
+	    con_puts("JTAG: firmware flash process failed\n");
+	} else {
 	    con_printf("JTAG: unknown command: 0x%08x\n", cmd);
-
-	con_puts("JTAG: starting firmware flash process\n");
-	rom_flash_from_memory(jtag_flash_buf, sizeof jtag_flash_buf);
+	}
     }
 }

+ 10 - 0
rv32/jtagupd.ld

@@ -15,6 +15,9 @@ OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
               "elf32-littleriscv")
 OUTPUT_ARCH(riscv)
 ENTRY(___reset)
+EXTERN(_NULL)
+EXTERN(_reset)
+EXTERN(_irq)
 
 MEMORY
 {
@@ -258,6 +261,13 @@ SECTIONS
 		  *(.jtag_flash_buffer)
 	} >DRAM
 
+	/* There is no dram init ... there can't be! */
+	. = ALIGN(8);
+	__dram_init_start = .;
+	__dram_init_end   = .;
+	__dram_init_len   = 0;
+
+	. = ALIGN(8);
 	__dram_bss_start = .;
 	.dram.bss (NOLOAD) : ALIGN(8) {
 		*(.dram.bss* .bss*)

+ 3 - 0
rv32/max80.ld

@@ -13,6 +13,9 @@ OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv",
               "elf32-littleriscv")
 OUTPUT_ARCH(riscv)
 ENTRY(___reset)
+EXTERN(_NULL)
+EXTERN(_reset)
+EXTERN(_irq)
 
 MEMORY
 {

+ 33 - 47
rv32/romcopy.c

@@ -3,44 +3,6 @@
 #include "io.h"
 #include "spiflash.h"
 
-enum romcmd {
-    ROM_WRITE_ENABLE			= 0x06,
-    ROM_VOLATILE_SR_WRITE_ENABLE	= 0x50,
-    ROM_WRITE_DISABLE			= 0x04,
-    ROM_RELEASE_POWERDOWN_ID		= 0xab,
-    ROM_MANUFACTURER_DEVICE_ID		= 0x90,
-    ROM_JEDEC_ID			= 0x9f,
-    ROM_READ_UNIQUE_ID			= 0x4b,
-    ROM_READ_DATA			= 0x03, /* DO NOT USE */
-    ROM_FAST_READ			= 0x0b,
-    ROM_PAGE_PROGRAM			= 0x02,
-    ROM_ERASE_4K			= 0x20,
-    ROM_ERASE_32K			= 0x52,
-    ROM_ERASE_64K			= 0xd8,
-    ROM_ERASE_ALL			= 0xc7,
-    ROM_READ_SR1			= 0x05,
-    ROM_WRITE_SR1			= 0x01,
-    ROM_READ_SR2			= 0x35,
-    ROM_WRITE_SR2			= 0x31,
-    ROM_READ_SR3			= 0x15,
-    ROM_WRITE_SR3			= 0x11,
-    ROM_READ_SFPD			= 0x5a,
-    ROM_ERASE_SECURITY			= 0x44,
-    ROM_PROGRAM_SECURITY		= 0x42,
-    ROM_READ_SECURITY			= 0x48,
-    ROM_GLOBAL_BLOCK_LOCK		= 0x7e,
-    ROM_GLOBAL_BLOCK_UNLOCK		= 0x98,
-    ROM_READ_BLOCK_LOCK			= 0x3d,
-    ROM_ONE_BLOCK_LOCK			= 0x36,
-    ROM_ONE_BLOCK_UNLOCK		= 0x39,
-    ROM_ERASE_PROGRAM_SUSPEND		= 0x75,
-    ROM_ERASE_PROGRAM_RESUME		= 0x7a,
-    ROM_POWER_DOWN			= 0xb9,
-    ROM_ENABLE_RESET			= 0x66,
-    ROM_RESET				= 0x99,
-    ROM_FAST_READ_DUAL			= 0x3b
-};
-
 #define SPIROM_DUAL_MODE 1
 
 void __hot romcopy_download(void *dst, size_t offset, size_t len)
@@ -48,6 +10,9 @@ void __hot romcopy_download(void *dst, size_t offset, size_t len)
     unsigned int cmd;
     unsigned int flags = ROMCOPY_SPI_CMDLEN(5) | ROMCOPY_WRITE_RAM;
 
+    if (!len)
+	return;
+    
     if (SPIROM_DUAL_MODE) {
 	cmd = ROM_FAST_READ_DUAL;
 	flags |= ROMCOPY_SPI_DUAL;
@@ -62,6 +27,9 @@ void __hot romcopy_download(void *dst, size_t offset, size_t len)
 
 void __hot romcopy_bzero(void *dst, size_t len)
 {
+    if (!len)
+	return;
+    
     ROMCOPY_RAMADDR = (size_t)dst;
     ROMCOPY_ROMCMD  = 0;
     ROMCOPY_DATALEN = len | ROMCOPY_ZERO_BUFFER | ROMCOPY_WRITE_RAM;
@@ -191,10 +159,12 @@ static int max80_spi_write_buffer(const void *buf, unsigned int buflen,
     const uint8_t *p = buf;
     uint32_t cmd = 0;
     unsigned int bytecmd = 0;
+    unsigned int bitpos = 24;
 
     while (buflen) {
-	cmd = (*p++ << 24) | (cmd >> 8);
+	cmd |= *p++ << bitpos;
 	buflen--;
+	bitpos -= 8;
 	bytecmd += ROMCOPY_SPI_CMDLEN(1);
 
 	if (!(buflen & 3)) {
@@ -204,6 +174,7 @@ static int max80_spi_write_buffer(const void *buf, unsigned int buflen,
 	    ROMCOPY_ROMCMD  = cmd;
 	    ROMCOPY_DATALEN = bytecmd;
 	    bytecmd = cmd = 0;
+	    bitpos = 24;
 	}
     }
 
@@ -241,12 +212,10 @@ static int max80_spi_read_buffer(void *buf, unsigned int buflen, bool more)
     ROMCOPY_DATALEN = bytecmd;
     waitfor(ROMCOPY_IRQ);
     v = ROMCOPY_INPUT;
-    v <<= ((4-buflen) << 3);
-    while (buflen--) {
-	*p++ = v >> 24;
-	v <<= 8;
+    while (buflen) {
+	p[--buflen] = v;
+	v >>= 8;
     }
-
     return 0;
 }
 
@@ -258,6 +227,9 @@ static int max80_spi_write(void *cookie,
     (void)cookie;
     (void)tshsl;		/* Enforced in hardware */
 
+    waitfor(ROMCOPY_IRQ);
+    udelay(1);
+
     if (cmd_len)
 	max80_spi_write_buffer(cmd, cmd_len, !!data_len);
 
@@ -275,8 +247,12 @@ static int max80_spi_read(void *cookie,
     (void)cookie;
     (void)tshsl;		/* Enforced in hardware */
 
+    waitfor(ROMCOPY_IRQ);
+    udelay(1);
+
     if (cmd_len)
 	max80_spi_write_buffer(cmd, cmd_len, !!data_len);
+
     if (data_len)
 	max80_spi_read_buffer(data, data_len, false);
 
@@ -329,12 +305,22 @@ static struct spiflash max80_flash = {
 void rom_flash_from_memory(void *buf, size_t buflen)
 {
     const struct spiflash_header *hdr = buf;
+    uint32_t romid[2];
 
-    if (hdr->magic != SPIFLASH_MAGIC)
+    for (int i = 0; i < 8; i++)
+	con_printf("%08x ", ((const uint32_t *)buf)[i]);
+    con_putc('\n');
+
+    if (hdr->magic != SPIFLASH_MAGIC) {
+	con_printf("update: no flash update data found in memory at %p\n", hdr);
 	return;
+    }
+
+    memset(romid, 0, sizeof romid);
+    spiflash_read_id(&max80_flash, romid);
+    con_printf("update: ROM serial number %08x-%08x\n", romid[0], romid[1]);
 
-    if (hdr->magic != SPIFLASH_MAGIC ||
-	spiflash_flash_files(&max80_flash, buf, buflen)) {
+    if (spiflash_flash_files(&max80_flash, buf, buflen)) {
 	con_puts("update: flash update data invalid\n");
 	return;
     }

+ 270 - 153
rv32/spiflash.c

@@ -20,6 +20,7 @@ struct spz_stream {
     /* Note: available output data ends at zs->next_out */
     uint8_t *ibuf;		    /* Input buffer if compressed */
     uint8_t *obuf;		    /* Output buffer */
+    uint8_t *dbuf;		    /* Block data buffer */
     uint8_t *vbuf;		    /* Readback/verify buffer */
     const struct spiflash_header *header;
     uint32_t crc32;		    /* Input data CRC32 */
@@ -149,10 +150,11 @@ static int spiflash_data_init(spz_stream *spz)
     int (*read_data)(void *cookie, void *buf, unsigned int bufsize);
     uint32_t header_crc;
 
-    MSG("update: initializing... ");
+    MSG("update: ");
 
+    spz->dbuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
     spz->vbuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
-    if (!spz->vbuf)
+    if (!spz->dbuf || !spz->vbuf)
 	goto err;
 
     rlen = spz->zs.avail_in;
@@ -169,8 +171,14 @@ static int spiflash_data_init(spz_stream *spz)
     }
     if (spz->err)
 	goto err;
+    if (!rlen) {
+	MSG("done");
+	spz->err = Z_STREAM_END;
+	goto not_err;
+    }
     if (rlen < (int)sizeof *spz->header) {
-	spz->err = rlen ? Z_STREAM_ERROR : Z_STREAM_END;
+	MSG("input underrun");
+	spz->err = Z_STREAM_ERROR;
 	goto err;
     }
 
@@ -181,25 +189,31 @@ static int spiflash_data_init(spz_stream *spz)
      * Check header magic and CRC
      */
     if (spz->header->magic != SPIFLASH_MAGIC) {
+	MSG("bad header magic");
 	spz->err = Z_STREAM_ERROR;
 	goto err;
     }
     header_crc = crc32(0, NULL, 0);
     header_crc = crc32(header_crc, (const void *)spz->header,
-		       sizeof spz->header - 4);
-    if (header_crc != spz->header->header_crc32 ||
-	spz->header->zlen > spz->header->dlen ||
-	(spz->eoi && (int)spz->header->zlen > rlen)) {
+		       sizeof *spz->header - 4);
+    if (header_crc != spz->header->header_crc32) {
+	MSG("header CRC error (0x%08x vs 0x%08x)...",
+	    header_crc, spz->header->header_crc32);
 	spz->err = Z_STREAM_ERROR;
 	goto err;
     }
     if (!spz->header->dlen) {
 	/* End of data */
 	spz->err = Z_STREAM_END;
+	goto not_err;
+    }
+    if (spz->header->zlen > spz->header->dlen ||
+	(spz->eoi && (int)spz->header->zlen > rlen)) {
+	spz->err = Z_STREAM_ERROR;
 	goto err;
     }
 
-    MSG("updating 0x%06x..%06x (%u bytes)... ",
+    MSG("data 0x%06x..0x%06x (%u bytes)\n",
 	spz->header->address, spz->header->address + spz->header->dlen - 1,
 	spz->header->dlen);
 
@@ -251,7 +265,11 @@ static int spiflash_data_init(spz_stream *spz)
     }
 
 err:
-    MSG("%s\n", spz->err ? "failed" : "ok");
+    if (spz->err)
+	MSG(" failed (err %d)\n", spz->err);
+
+not_err:
+    MSG("\n");
     return spz->err;
  }
 
@@ -276,6 +294,8 @@ static int spiflash_data_cleanup(spz_stream *spz)
 	free((void *)spz->header);
     if (spz->vbuf)
 	free(spz->vbuf);
+    if (spz->dbuf)
+	free(spz->dbuf);
     if (spz->obuf)
 	free(spz->obuf);
     if (spz->ibuf)
@@ -301,7 +321,6 @@ static void *spiflash_setup_addrcmd(const struct spiflash *flash,
 	mode = addr < (1 << 24) ? SPIFLASH_ADDR_24BIT : SPIFLASH_ADDR_32BIT;
 
     if (mode == SPIFLASH_ADDR_24BIT) {
-	addr <<= 8;
 	*cmd++ = cmd24;
     } else {
 	*cmd++ = cmd32;
@@ -314,19 +333,28 @@ static void *spiflash_setup_addrcmd(const struct spiflash *flash,
     return cmd;
 }
 
-static int spiflash_wait_ready(const struct spiflash *flash, int delay)
+static int spiflash_get_status(const struct spiflash *flash,
+			       uint8_t cmd, uint8_t *sr)
+{
+    return flash->ops->spi_read(flash->cookie, &cmd, 1, sr, 1,
+				flash->param->tshsl);
+}
+
+/* This needs a timeout function */
+static int spiflash_wait_status(const struct spiflash *flash,
+				int delay, uint8_t mask, uint8_t val)
 {
-    uint8_t cmd = 0x05;		/* Read Status Register 1 */
-    uint8_t sr1 = 0;
+    uint8_t sr1;
     int rv;
 
     do {
-	flash->ops->yield(flash->cookie, delay);
-	rv = flash->ops->spi_read(flash->cookie, &cmd, 1,
-				  &sr1, 1, flash->param->tshsl);
+	if (flash->ops->yield)
+	    flash->ops->yield(flash->cookie, delay);
+
+	rv = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
 	if (rv)
 	    return rv;
-    } while (sr1 & 0x01);	/* Busy bit set? */
+    } while ((sr1 & mask) != val); /* Waiting... */
 
     return 0;
 }
@@ -337,25 +365,37 @@ int spiflash_read(const struct spiflash *flash,
     uint8_t cmdbuf[6];
     uint8_t *cmd;
 
-    /*
-     * 13h = Fast Read
-     * 0Ch = Fast Read with 4-Byte Address
-     */
-    cmd = spiflash_setup_addrcmd(flash, addr, 0x13, 0x0c, cmdbuf);
-    *cmd++ = 0;			/* Dummy bits */
+    cmd = spiflash_setup_addrcmd(flash, addr,
+				 ROM_FAST_READ, ROM_FAST_READ_32BIT,
+				 cmdbuf);
+    *cmd++ = 0;			/* Dummy cycles */
 
     return flash->ops->spi_read(flash->cookie, cmdbuf, cmd - cmdbuf,
 				buffer, len, flash->param->tshsl1);
 }
 
-static int spiflash_write_enable(const struct spiflash *flash)
+static int spiflash_simple_command(const struct spiflash *flash, uint8_t cmd)
 {
-    const uint8_t cmd = 0x06;	/* 06h = Write Enable */
-
     return flash->ops->spi_write(flash->cookie, &cmd, 1, NULL, 0,
 				 flash->param->tshsl);
 }
 
+static int spiflash_write_enable(const struct spiflash *flash)
+{
+    uint8_t sr1;
+    int rv;
+
+    rv = spiflash_wait_status(flash, 0, 1, 0);
+    if (rv)
+	return rv;
+
+    rv = spiflash_simple_command(flash, ROM_WRITE_ENABLE);
+    if (rv)
+	return rv;
+
+    return spiflash_wait_status(flash, 0, 3, 2);
+}
+
 static int spiflash_program(const struct spiflash *flash,
 			    uint32_t addr, const void *buffer, size_t len)
 {
@@ -367,17 +407,15 @@ static int spiflash_program(const struct spiflash *flash,
     if (rv)
 	return rv;
 
-    /*
-     * 02h = Page Program
-     * 12h = Page Program with 4-Byte Address
-     */
-    cmd = spiflash_setup_addrcmd(flash, addr, 0x02, 0x12, cmdbuf);
+    cmd = spiflash_setup_addrcmd(flash, addr,
+				 ROM_PAGE_PROGRAM, ROM_PAGE_PROGRAM_32BIT,
+				 cmdbuf);
     rv = flash->ops->spi_write(flash->cookie, cmdbuf, cmd - cmdbuf,
 			       buffer, len, flash->param->tshsl2);
     if (rv)
 	return rv;
 
-    return spiflash_wait_ready(flash, flash->param->tpp);
+    return spiflash_wait_status(flash, flash->param->tpp, 3, 0);
 }
 
 /*
@@ -389,56 +427,55 @@ static int spiflash_erase(const struct spiflash *flash,
     uint8_t cmdbuf[5];
     uint8_t *cmd;
     uint8_t cmd24, cmd32;
-    uint32_t nextaddr;
+    uint32_t erasesize;
     int rv;
     int delay;
     const uint32_t block_mask = SPIFLASH_BLOCK_SIZE - 1;
-    const unsigned long block_sectors = block_mask >> SPIFLASH_SECTOR_SHIFT;
+    const unsigned long block_sector_mask
+	= block_mask >> SPIFLASH_SECTOR_SHIFT;
 
+    if (!sector_mask) {
+	MSG("update: nothing to erase\n");
+	return 0;
+    }
+    
     while (sector_mask) {
-	if (((sector_mask & block_sectors) == block_sectors) &&
-	    !(addr & block_mask)) {
-		/*
-		 * D8h = 64KB Block Erase
-		 * DCh = 64K Block Erase with 4-Byte Address
-		 */
-		cmd24 = 0xd8; cmd32 = 0xdc;
+	if (!(addr & block_mask) &&
+	    ((sector_mask & block_sector_mask) == block_sector_mask)) {
+		cmd24 = ROM_ERASE_64K;
+		cmd32 = ROM_ERASE_64K_32BIT;
 		delay = flash->param->tbe2;
-		nextaddr = addr + SPIFLASH_BLOCK_SIZE;
-		sector_mask >>= 16;
+		erasesize = SPIFLASH_BLOCK_SIZE;
 	} else {
-	    if (sector_mask & 1) {
-		/*
-		 * 20h = Sector Erase
-		 * 21h = Sector Erase with 4-Byte Address
-		 */
-		cmd24 = 0x20; cmd32 = 0x21;
-		delay = flash->param->tse;
-		nextaddr = addr + SPIFLASH_SECTOR_SIZE;
-		sector_mask >>= 1;
-	    } else {
-		addr += SPIFLASH_SECTOR_SIZE;
-		sector_mask >>= 1;
-		continue;	/* Skip sector */
-	    }
+	    cmd24 = ROM_ERASE_4K;
+	    cmd32 = ROM_ERASE_4K_32BIT;
+	    delay = flash->param->tse;
+	    erasesize = SPIFLASH_SECTOR_SIZE;
 	}
-	rv = spiflash_write_enable(flash);
-	if (rv)
-	    return rv;
 
-	cmd = spiflash_setup_addrcmd(flash, addr, cmd24, cmd32, cmdbuf);
-	rv = flash->ops->spi_write(flash->cookie, cmdbuf, cmd - cmdbuf,
-				   NULL, 0, flash->param->tshsl2);
-	if (rv)
-	    return rv;
+	if (sector_mask & 1) {
+	    MSG("\rupdate: erasing %2uK at 0x%06x... ", erasesize >> 10, addr);
+	
+	    rv = spiflash_write_enable(flash);
+	    if (rv)
+		return rv;
 
-	rv = spiflash_wait_ready(flash, delay);
-	if (rv)
-	    return rv;
+	    cmd = spiflash_setup_addrcmd(flash, addr, cmd24, cmd32, cmdbuf);
+	    rv = flash->ops->spi_write(flash->cookie, cmdbuf, cmd - cmdbuf,
+				       NULL, 0, flash->param->tshsl2);
+	    if (rv)
+		return rv;
+
+	    rv = spiflash_wait_status(flash, delay, 3, 0);
+	    if (rv)
+		return rv;
+	}
 
-	addr = nextaddr;
+	addr += erasesize;
+	sector_mask >>= (erasesize >> SPIFLASH_SECTOR_SHIFT);
     }
 
+    MSG("ok\n");
     return 0;
 }
 
@@ -460,26 +497,26 @@ spiflash_memcmp(const void *from, const void *to, size_t len)
     const uint32_t *pf = from;
     const uint32_t *pt = to;
     const uint32_t *pfend = (const uint32_t *)((const char *)from + len);
-    uint32_t notok     = 0;
-    uint32_t notprog   = 0;
+    uint32_t doprog  = 0;
+    uint32_t doerase = 0;
 
     while (pf < pfend) {
 	uint32_t f = *pf++;
 	uint32_t t = *pt++;
 
-	notok   |=  f ^ t;	/* Need programming if any data mismatch */
-	notprog |= ~f & t;	/* Need erasing if any 0 -> 1 */
+	doprog  |=  f ^ t;	/* Need programming if any data mismatch */
+	doerase |= ~f & t;	/* Need erasing if any 0 -> 1 */
     }
 
-    return notprog ? FMS_ERASE : notok ? FMS_PROGRAM : FMS_DONE;
+    return doerase ? FMS_ERASE : doprog ? FMS_PROGRAM : FMS_DONE;
 }
 
 /*
  * Check a block for sectors which need erasing and pages which need
  * programming; the prog_mask is 256 bits long and so span multiple words.
  *
- * The input is spz->optr, and the existing flash content is written
- * to spz->vptr.
+ * The desired input is spz->dbuf and the existing flash content is
+ * written to spz->vbuf.
  *
  */
 static int spiflash_check_block(spz_stream *spz, uint32_t addr,
@@ -496,8 +533,11 @@ static int spiflash_check_block(spz_stream *spz, uint32_t addr,
 	return rv;
     }
 
-    p = spz->optr;
-    q = spz->vbuf;
+    *erase_mask = 0;
+    memset(prog_mask, 0, SPIFLASH_BLOCK_SIZE/SPIFLASH_PAGE_SIZE/8);
+    
+    p = spz->vbuf;
+    q = spz->dbuf;
 
     for (page = 0; page < SPIFLASH_BLOCK_SIZE/SPIFLASH_PAGE_SIZE; page++) {
 	enum flashmem_status status;
@@ -522,19 +562,87 @@ static int spiflash_check_block(spz_stream *spz, uint32_t addr,
     return 0;
 }
 
+/* Serial Flash Discoverable Parameter Table, see JESD216 */
+static int spiflash_get_sfdp(const struct spiflash *flash, void *sfdp)
+{
+    static const uint8_t cmd_read_sfdp[] = { ROM_READ_SFDP, 0, 0, 0, 0 };
+
+    return flash->ops->spi_read(flash->cookie, cmd_read_sfdp,
+				sizeof cmd_read_sfdp, sfdp, SPIFLASH_SFDP_SIZE,
+				flash->param->tshsl);
+}
+    
+
 int spiflash_flash_files(const struct spiflash *flash, void *buf, size_t buflen)
 {
     spz_stream _spz;
     spz_stream * const spz = &_spz; /* For consistency in notation */
-    int  err;
+    int  err = 0;
     enum flashmem_status fs;
-    uint8_t *padbuf = NULL;
 
-    err = 0;
+#if 0
+    static const uint8_t read_sr_cmd[2] = { ROM_READ_SR1, ROM_READ_SR2 };
+    uint8_t sr1;
+    uint32_t *sfdp;
+
+    sfdp = malloc(SPIFLASH_SFDP_SIZE);
+    memset(sfdp, 0, SPIFLASH_SFDP_SIZE);
+
+    /* Note: SFDP data is littleendian! */
+    err = spiflash_get_sfdp(flash, sfdp);
+    if (err)
+	return err;
+
+    for (int i = 0; i < SPIFLASH_SFDP_SIZE; i += 16) {
+	MSG("%04x :", i);
+	for (int j = 0; j < 16; j += 4) {
+	    MSG(" %08x", sfdp[(i+j) >> 2]);
+	}
+	MSG("\n");
+    }
+    
+    if (sfdp[0] != 0x50444653) {
+	MSG("update: invalid SFDP information read\n");
+	return SPIFLASH_ERR_DETECT;
+    }
+
+    /*
+     * If the flash is busy, try to reset it
+     */
+    err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
+    if (err)
+	return err;
+    if (sr1 & 0x01) {
+	udelay(60);
+	err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
+	if (err)
+	    return err;
+	if (sr1 & 0x01) {
+	    MSG("update: flash busy, trying reset... ");
+	    
+	    err = spiflash_simple_command(flash, ROM_ENABLE_RESET);
+	    if (err)
+		return err;
+	    err = spiflash_simple_command(flash, ROM_RESET);
+	    if (err)
+		return err;
+
+	    udelay(60);
+	    err = spiflash_get_status(flash, ROM_READ_SR1, &sr1);
+	    if (err || (sr1 & 0x01)) {
+		MSG("failed\n");
+		return SPIFLASH_ERR_NOT_READY;
+	    }
+	    MSG("ok\n");
+	}
+    }
+
+#endif
+    
     while (!err) {
 	int rv;
-	bool eof;
 	uint32_t addr;
+	uint32_t data_left;
 
 	memset(spz, 0, sizeof *spz);
 	spz->zs.avail_in = buflen;
@@ -546,13 +654,13 @@ int spiflash_flash_files(const struct spiflash *flash, void *buf, size_t buflen)
 
 	if (!spz->ibuf) {
 	    /* No ibuf allocated, feeding from raw data buffer */
-	    unsigned int bufskip = (spz->zs.next_out - (uint8_t *)buf + 3) & ~3;
+	    unsigned int bufskip = (spz->header->zlen + sizeof *spz->header + 3) & ~3;
 	    if (bufskip >= buflen) {
 		buflen = 0;
 		buf = NULL;
 	    } else {
 		buflen -= bufskip;
-		buf = spz->zs.next_out;
+		buf += bufskip;
 	    }
 	} else {
 	    /* Buffer exhausted, additional data read */
@@ -560,39 +668,48 @@ int spiflash_flash_files(const struct spiflash *flash, void *buf, size_t buflen)
 	    buf = NULL;
 	}
 
-	eof = false;
-	addr = spz->header->address;
+	data_left = spz->header->dlen;
+	addr      = spz->header->address;
 
-	while (!eof && !spz->err) {
-	    unsigned int bytes = spz->zs.next_out - spz->optr;
+	while (data_left && !spz->err) {
+	    unsigned int bytes = 0;
 	    unsigned int padding;
-
-	    if (bytes < SPIFLASH_BLOCK_SIZE) {
+	    
+	    while (data_left && bytes < SPIFLASH_BLOCK_SIZE) {
+		unsigned int avail = spz->zs.next_out - spz->optr;
+		unsigned int need = SPIFLASH_BLOCK_SIZE - bytes;
 		int rv;
 
+		if (need > data_left)
+		    need = data_left;
+		
+		if (avail) {
+		    if (avail > need)
+			avail = need;
+
+		    memcpy(spz->dbuf + bytes, spz->optr, avail);
+		    spz->optr += avail;
+		    bytes     += avail;
+		    data_left -= avail;
+		    continue;
+		}
+		
 		rv = spz->read_data(spz);
+
 		if (spz->err)
 		    goto err;
-		eof = !rv;
-		bytes = spz->zs.next_out - spz->optr;
-		if (!bytes)
-		    break;
-
-		padding = -bytes & (SPIFLASH_BLOCK_SIZE-1);
-		if (padding) {
-		    if (!padbuf) {
-			padbuf = spz_malloc(spz, SPIFLASH_BLOCK_SIZE);
-			if (!padbuf)
-			    goto err;
-		    }
-		    memcpy(padbuf, spz->optr, bytes);
-		    memset(padbuf+bytes, 0xff, padding);
-		    spz->optr = padbuf;
-		    eof = true;
+
+		if (!rv) {
+		    spz->err = Z_STREAM_ERROR; /* Input underrun */
+		    goto err;
 		}
 	    }
 
-	    MSG("update: flash block at 0x%06x: ", addr);
+	    if (bytes < SPIFLASH_BLOCK_SIZE) {
+		memset(spz->dbuf + bytes, 0xff, SPIFLASH_BLOCK_SIZE - bytes);
+	    }
+
+	    MSG("update: flash block at 0x%06x (%5u bytes):\n", addr, bytes);
 
 	    uint32_t erase_mask;
 	    uint32_t prog_mask[SPIFLASH_BLOCK_SIZE >> (SPIFLASH_PAGE_SHIFT+5)];
@@ -602,8 +719,6 @@ int spiflash_flash_files(const struct spiflash *flash, void *buf, size_t buflen)
 		goto err;
 
 	    if (erase_mask) {
-		MSG("erasing... ");
-
 		rv = spiflash_erase(spz->flash, addr, erase_mask);
 		if (rv) {
 		    spz->err = rv;
@@ -614,69 +729,71 @@ int spiflash_flash_files(const struct spiflash *flash, void *buf, size_t buflen)
 		if (spz->err)
 		    goto err;
 		if (erase_mask) {
+		    MSG("[erase mask = %04x] ", erase_mask);
 		    spz->err = SPIFLASH_ERR_ERASE_FAILED;
 		    goto err;
 		}
 	    }
 
 	    unsigned int page;
-	    bool first_prog = true;
+	    bool programmed = false;
 
 	    for (page = 0; page < (SPIFLASH_BLOCK_SIZE >> SPIFLASH_PAGE_SHIFT);
 		 page++) {
 		uint32_t page_offs = page << SPIFLASH_PAGE_SHIFT;
 
-		if (prog_mask[page >> 5] & (UINT32_C(1) << (page & 31))) {
-
-		    if (first_prog) {
-			MSG("programming %06x... ", addr + page_offs);
-		    } else {
-			MSG("\b\b\b\b\b\b\b\b\b\b%06x... ", addr+page_offs);
-		    }
-		    first_prog = false;
-
-		    rv = spiflash_program(spz->flash, addr + page_offs,
-					  spz->optr + page_offs,
-					  SPIFLASH_PAGE_SIZE);
-		    if (rv) {
-			spz->err = rv;
-			goto err;
-		    }
-
-		    /* Read back data and verify */
-		    rv = spiflash_read(spz->flash, addr+page_offs,
-				       spz->vbuf + page_offs,
-				       SPIFLASH_PAGE_SIZE);
-		    if (rv) {
-			MSG("verify "); /* "verify failed" */
-			spz->err = rv;
-			goto err;
-		    }
-
-		    if (memcmp(spz->optr + page_offs, spz->vbuf + page_offs,
-			       SPIFLASH_PAGE_SIZE)) {
-			spz->err = SPIFLASH_ERR_PROGRAM_FAILED;
-			goto err;
-		    }
+		if (!(prog_mask[page >> 5] & (UINT32_C(1) << (page & 31))))
+		    continue;	/* No need to program */
+
+		programmed = true;
+
+		udelay(10000);
+		
+		MSG("\rupdate: writing at 0x%06x... ", addr + page_offs);
+
+		rv = spiflash_program(spz->flash, addr + page_offs,
+				      spz->dbuf + page_offs,
+				      SPIFLASH_PAGE_SIZE);
+		if (rv) {
+		    spz->err = rv;
+		    goto err;
 		}
-	    }
 
-	    spz->optr += SPIFLASH_BLOCK_SIZE;
-	    addr      += SPIFLASH_BLOCK_SIZE;
+		rv = spiflash_read(spz->flash, addr + page_offs,
+				   spz->vbuf + page_offs,
+				   SPIFLASH_PAGE_SIZE);
+		if (rv) {
+		    MSG("readback ");
+		    goto err;
+		}
+
+
+		if (memcmp(spz->dbuf + page_offs, spz->vbuf + page_offs,
+			   SPIFLASH_PAGE_SIZE)) {
+		    MSG("verify @ 0x%06x ", addr + page_offs);
+		    spz->err = SPIFLASH_ERR_PROGRAM_FAILED;
+		    goto err;
+		}
+	    }
+	    if (programmed)
+		MSG("ok\n");
+	    else
+		MSG("update: nothing to write\n");
+	    
+	    addr += SPIFLASH_BLOCK_SIZE;
 	}
 
     err:
 	err = spiflash_data_cleanup(spz);
     }
 
-    if (padbuf) {
-	free(padbuf);
-	padbuf = NULL;
-    }
+    if (err == Z_STREAM_END)
+	err = 0;		/* End of data is not an error */
 
-    MSG("%s\n", spz->err ? "failed" : "ok");
+    if (err)
+	MSG("failed (err %d)\n", err);
 
-    return spz->err;
+    return err;
 }
 
 /*
@@ -685,7 +802,7 @@ int spiflash_flash_files(const struct spiflash *flash, void *buf, size_t buflen)
  */
 int spiflash_read_id(const struct spiflash *flash, void *id)
 {
-    static const uint8_t read_unique_id[] = { 0x4b, 0, 0, 0, 0 };
+    static const uint8_t read_unique_id[] = { ROM_READ_UNIQUE_ID, 0, 0, 0, 0 };
 
     return flash->ops->spi_read(flash->cookie, read_unique_id,
 				sizeof read_unique_id,
@@ -697,7 +814,7 @@ int spiflash_read_id(const struct spiflash *flash, void *id)
  */
 int spiflash_read_vdid(const struct spiflash *flash, void *vdid)
 {
-    static const uint8_t read_vdid[] = { 0x90, 0, 0, 0 };
+    static const uint8_t read_vdid[] = { ROM_MANUFACTURER_DEVICE_ID, 0, 0, 0 };
 
     return flash->ops->spi_read(flash->cookie, read_vdid,
 				sizeof read_vdid,

+ 60 - 2
rv32/spiflash.h

@@ -5,6 +5,61 @@
 #include <stddef.h>
 #include <string.h>
 
+/* SPI flash command opcodes */
+enum romcmd {
+    /* Standard SPI mode commands */
+    ROM_WRITE_ENABLE			= 0x06,
+    ROM_VOLATILE_SR_WRITE_ENABLE	= 0x50,
+    ROM_WRITE_DISABLE			= 0x04,
+    ROM_RELEASE_POWERDOWN_ID		= 0xab,
+    ROM_MANUFACTURER_DEVICE_ID		= 0x90,
+    ROM_JEDEC_ID			= 0x9f,
+    ROM_READ_UNIQUE_ID			= 0x4b,
+    ROM_READ_DATA			= 0x03, /* DO NOT USE */
+    ROM_READ_DATA_32BIT			= 0x13, /* DO NOT USE */
+    ROM_FAST_READ			= 0x0b,
+    ROM_FAST_READ_32BIT			= 0x0c,
+    ROM_PAGE_PROGRAM			= 0x02,
+    ROM_PAGE_PROGRAM_32BIT		= 0x12,
+    ROM_ERASE_4K			= 0x20,
+    ROM_ERASE_4K_32BIT			= 0x21,
+    ROM_ERASE_32K			= 0x52,
+    ROM_ERASE_64K			= 0xd8,
+    ROM_ERASE_64K_32BIT			= 0xdc,
+    ROM_ERASE_ALL			= 0xc7,
+    ROM_READ_SR1			= 0x05,
+    ROM_WRITE_SR1			= 0x01,
+    ROM_READ_SR2			= 0x35,
+    ROM_WRITE_SR2			= 0x31,
+    ROM_READ_SR3			= 0x15,
+    ROM_WRITE_SR3			= 0x11,
+    ROM_READ_EAR			= 0xc8, /* Extended address register */
+    ROM_WRITE_EAR			= 0xc5,
+    ROM_READ_SFDP			= 0x5a,
+    ROM_ERASE_SECURITY			= 0x44,
+    ROM_PROGRAM_SECURITY		= 0x42,
+    ROM_READ_SECURITY			= 0x48,
+    ROM_GLOBAL_BLOCK_LOCK		= 0x7e,
+    ROM_GLOBAL_BLOCK_UNLOCK		= 0x98,
+    ROM_READ_BLOCK_LOCK			= 0x3d,
+    ROM_ONE_BLOCK_LOCK			= 0x36,
+    ROM_ONE_BLOCK_UNLOCK		= 0x39,
+    ROM_ERASE_PROGRAM_SUSPEND		= 0x75,
+    ROM_ERASE_PROGRAM_RESUME		= 0x7a,
+    ROM_POWER_DOWN			= 0xb9,
+    ROM_ENTER_32BIT			= 0xb7,
+    ROM_LEAVE_32BIT			= 0xe9,
+    ROM_ENTER_QPI			= 0x48,
+    ROM_ENABLE_RESET			= 0x66,
+    ROM_RESET				= 0x99,
+
+    /* Dual SPI commands */
+    ROM_FAST_READ_DUAL			= 0x3b,
+    ROM_FAST_READ_DUAL_32BIT		= 0x3c
+};
+
+#define SPIFLASH_SFDP_SIZE	256
+
 /*
  * Firmware blob data header. A dlen of 0 means no futher blobs.
  * dlen == zlen means raw binary data, not compressed.
@@ -23,13 +78,13 @@ struct spiflash_header {
 };
 
 /*
- * A page is the maximum amount that can be programmed in one operation.
+ * A page is an amount that can be programmed in one operation.
  * A sector is the minimum amount that can be erased in one operation.
  * A block is the optimal amount that can be erased in one operation.
  *
  * These are defined in hardware!
  */
-#define SPIFLASH_PAGE_SHIFT	8
+#define SPIFLASH_PAGE_SHIFT	8 /* May be smaller than an actual page */
 #define SPIFLASH_PAGE_SIZE	(1 << SPIFLASH_PAGE_SHIFT)
 #define SPIFLASH_SECTOR_SHIFT	12
 #define SPIFLASH_SECTOR_SIZE	(1 << SPIFLASH_SECTOR_SHIFT)
@@ -216,6 +271,9 @@ struct spiflash {
  */
 #define SPIFLASH_ERR_ERASE_FAILED	(-7)
 #define SPIFLASH_ERR_PROGRAM_FAILED	(-8)
+#define SPIFLASH_ERR_WRITE_PROTECT	(-9)
+#define SPIFLASH_ERR_NOT_READY		(-10)
+#define SPIFLASH_ERR_DETECT		(-11)
 
 /*
  * Top-level operations. These may return an error value from the ops

+ 1 - 1
rv32/sys.h

@@ -25,6 +25,6 @@
 #define SDRAM_END       (SDRAM_ADDR + SDRAM_SIZE)
 
 /* Starting offset in flash for non-FPGA use */
-#define ROM_OFFSET	0x200000
+#define ROM_OFFSET	0x100000
 
 #endif /* SYS_H */

+ 1 - 1
tools/wrapflash.pl

@@ -5,7 +5,7 @@ use integer;
 use Digest::CRC;
 
 my $MAX_DATA_LEN = 16 << 20;
-my $SPIFLASH_MAGIC = 0xe201e7eb;
+my $SPIFLASH_MAGIC = 0xe301e7eb;
 
 sub getint($) {
     my($s) = @_;