diff --git a/fw/project/lcmxo2/sc64.ldf b/fw/project/lcmxo2/sc64.ldf
index bfead05..ebcf509 100644
--- a/fw/project/lcmxo2/sc64.ldf
+++ b/fw/project/lcmxo2/sc64.ldf
@@ -78,12 +78,21 @@
+
+
+
diff --git a/fw/project/lcmxo2/sc64.sty b/fw/project/lcmxo2/sc64.sty
index d06a191..aa5494f 100644
--- a/fw/project/lcmxo2/sc64.sty
+++ b/fw/project/lcmxo2/sc64.sty
@@ -92,7 +92,7 @@
-
+
@@ -119,7 +119,7 @@
-
+
diff --git a/fw/rtl/mcu/mcu_top.sv b/fw/rtl/mcu/mcu_top.sv
index b13f6ba..c3ef7a0 100644
--- a/fw/rtl/mcu/mcu_top.sv
+++ b/fw/rtl/mcu/mcu_top.sv
@@ -9,6 +9,7 @@ module mcu_top (
sd_scb.controller sd_scb,
dma_scb.controller sd_dma_scb,
flash_scb.controller flash_scb,
+ vendor_scb.controller vendor_scb,
fifo_bus.controller fifo_bus,
mem_bus.controller mem_bus,
@@ -353,7 +354,9 @@ module mcu_top (
REG_DD_CMD_DATA,
REG_DD_HEAD_TRACK,
REG_DD_SECTOR_INFO,
- REG_DD_DRIVE_ID
+ REG_DD_DRIVE_ID,
+ REG_VENDOR_SCR,
+ REG_VENDOR_DATA
} reg_address_e;
logic bootloader_skip;
@@ -579,6 +582,14 @@ module mcu_top (
dd_scb.sector_num
};
end
+
+ REG_VENDOR_SCR: begin
+ reg_rdata <= vendor_scb.control_rdata;
+ end
+
+ REG_VENDOR_DATA: begin
+ reg_rdata <= vendor_scb.data_rdata;
+ end
endcase
end
end
@@ -614,6 +625,8 @@ module mcu_top (
dd_scb.bm_clear <= 1'b0;
dd_scb.bm_ready <= 1'b0;
+ vendor_scb.control_valid <= 1'b0;
+
if (n64_scb.n64_nmi) begin
n64_scb.bootloader_enabled <= !bootloader_skip;
end
@@ -792,6 +805,15 @@ module mcu_top (
REG_DD_DRIVE_ID: begin
dd_scb.drive_id <= reg_wdata[15:0];
end
+
+ REG_VENDOR_SCR: begin
+ vendor_scb.control_valid <= 1'b1;
+ vendor_scb.control_wdata <= reg_wdata;
+ end
+
+ REG_VENDOR_DATA: begin
+ vendor_scb.data_wdata <= reg_wdata;
+ end
endcase
end
end
diff --git a/fw/rtl/top.sv b/fw/rtl/top.sv
index bc05538..685d724 100644
--- a/fw/rtl/top.sv
+++ b/fw/rtl/top.sv
@@ -58,6 +58,7 @@ module top (
sd_scb sd_scb ();
dma_scb sd_dma_scb ();
flash_scb flash_scb ();
+ vendor_scb vendor_scb ();
fifo_bus usb_cfg_fifo_bus ();
fifo_bus usb_dma_fifo_bus ();
@@ -93,6 +94,7 @@ module top (
.sd_scb(sd_scb),
.sd_dma_scb(sd_dma_scb),
.flash_scb(flash_scb),
+ .vendor_scb(vendor_scb),
.fifo_bus(usb_cfg_fifo_bus),
.mem_bus(cfg_mem_bus),
@@ -250,4 +252,14 @@ module top (
.mem_bus(bram_mem_bus)
);
+
+ // Vendor specific control
+
+ vendor vendor_inst (
+ .clk(clk),
+ .reset(reset),
+
+ .vendor_scb(vendor_scb)
+ );
+
endmodule
diff --git a/fw/rtl/vendor/lcmxo2/generated/efb_lattice_generated.v b/fw/rtl/vendor/lcmxo2/generated/efb_lattice_generated.v
new file mode 100644
index 0000000..17d16e8
--- /dev/null
+++ b/fw/rtl/vendor/lcmxo2/generated/efb_lattice_generated.v
@@ -0,0 +1,113 @@
+/* Verilog netlist generated by SCUBA Diamond (64-bit) 3.12.1.454 */
+/* Module Version: 1.2 */
+/* C:\lscc\diamond\3.12\ispfpga\bin\nt64\scuba.exe -w -n efb_lattice_generated -lang verilog -synth synplify -bus_exp 7 -bb -type efb -arch xo2c00 -freq 100 -ufm -ufm_ebr 2038 -mem_size 8 -ufm_0 -wb -dev 7000 */
+/* Sun Jul 31 17:59:17 2022 */
+
+
+`timescale 1 ns / 1 ps
+module efb_lattice_generated (wb_clk_i, wb_rst_i, wb_cyc_i, wb_stb_i,
+ wb_we_i, wb_adr_i, wb_dat_i, wb_dat_o, wb_ack_o, wbc_ufm_irq)/* synthesis NGD_DRC_MASK=1 */;
+ input wire wb_clk_i;
+ input wire wb_rst_i;
+ input wire wb_cyc_i;
+ input wire wb_stb_i;
+ input wire wb_we_i;
+ input wire [7:0] wb_adr_i;
+ input wire [7:0] wb_dat_i;
+ output wire [7:0] wb_dat_o;
+ output wire wb_ack_o;
+ output wire wbc_ufm_irq;
+
+ wire scuba_vhi;
+ wire scuba_vlo;
+
+ VHI scuba_vhi_inst (.Z(scuba_vhi));
+
+ VLO scuba_vlo_inst (.Z(scuba_vlo));
+
+ defparam EFBInst_0.UFM_INIT_FILE_FORMAT = "HEX" ;
+ defparam EFBInst_0.UFM_INIT_FILE_NAME = "NONE" ;
+ defparam EFBInst_0.UFM_INIT_ALL_ZEROS = "ENABLED" ;
+ defparam EFBInst_0.UFM_INIT_START_PAGE = 2038 ;
+ defparam EFBInst_0.UFM_INIT_PAGES = 8 ;
+ defparam EFBInst_0.DEV_DENSITY = "7000L" ;
+ defparam EFBInst_0.EFB_UFM = "ENABLED" ;
+ defparam EFBInst_0.TC_ICAPTURE = "DISABLED" ;
+ defparam EFBInst_0.TC_OVERFLOW = "DISABLED" ;
+ defparam EFBInst_0.TC_ICR_INT = "OFF" ;
+ defparam EFBInst_0.TC_OCR_INT = "OFF" ;
+ defparam EFBInst_0.TC_OV_INT = "OFF" ;
+ defparam EFBInst_0.TC_TOP_SEL = "OFF" ;
+ defparam EFBInst_0.TC_RESETN = "ENABLED" ;
+ defparam EFBInst_0.TC_OC_MODE = "TOGGLE" ;
+ defparam EFBInst_0.TC_OCR_SET = 32767 ;
+ defparam EFBInst_0.TC_TOP_SET = 65535 ;
+ defparam EFBInst_0.GSR = "ENABLED" ;
+ defparam EFBInst_0.TC_CCLK_SEL = 1 ;
+ defparam EFBInst_0.TC_MODE = "CTCM" ;
+ defparam EFBInst_0.TC_SCLK_SEL = "PCLOCK" ;
+ defparam EFBInst_0.EFB_TC_PORTMODE = "WB" ;
+ defparam EFBInst_0.EFB_TC = "DISABLED" ;
+ defparam EFBInst_0.SPI_WAKEUP = "DISABLED" ;
+ defparam EFBInst_0.SPI_INTR_RXOVR = "DISABLED" ;
+ defparam EFBInst_0.SPI_INTR_TXOVR = "DISABLED" ;
+ defparam EFBInst_0.SPI_INTR_RXRDY = "DISABLED" ;
+ defparam EFBInst_0.SPI_INTR_TXRDY = "DISABLED" ;
+ defparam EFBInst_0.SPI_SLAVE_HANDSHAKE = "DISABLED" ;
+ defparam EFBInst_0.SPI_PHASE_ADJ = "DISABLED" ;
+ defparam EFBInst_0.SPI_CLK_INV = "DISABLED" ;
+ defparam EFBInst_0.SPI_LSB_FIRST = "DISABLED" ;
+ defparam EFBInst_0.SPI_CLK_DIVIDER = 2 ;
+ defparam EFBInst_0.SPI_MODE = "MASTER" ;
+ defparam EFBInst_0.EFB_SPI = "DISABLED" ;
+ defparam EFBInst_0.I2C2_WAKEUP = "DISABLED" ;
+ defparam EFBInst_0.I2C2_GEN_CALL = "DISABLED" ;
+ defparam EFBInst_0.I2C2_CLK_DIVIDER = 1 ;
+ defparam EFBInst_0.I2C2_BUS_PERF = "100kHz" ;
+ defparam EFBInst_0.I2C2_SLAVE_ADDR = "0b1000010" ;
+ defparam EFBInst_0.I2C2_ADDRESSING = "7BIT" ;
+ defparam EFBInst_0.EFB_I2C2 = "DISABLED" ;
+ defparam EFBInst_0.I2C1_WAKEUP = "DISABLED" ;
+ defparam EFBInst_0.I2C1_GEN_CALL = "DISABLED" ;
+ defparam EFBInst_0.I2C1_CLK_DIVIDER = 1 ;
+ defparam EFBInst_0.I2C1_BUS_PERF = "100kHz" ;
+ defparam EFBInst_0.I2C1_SLAVE_ADDR = "0b1000001" ;
+ defparam EFBInst_0.I2C1_ADDRESSING = "7BIT" ;
+ defparam EFBInst_0.EFB_I2C1 = "DISABLED" ;
+ defparam EFBInst_0.EFB_WB_CLK_FREQ = "100.0" ;
+ EFB EFBInst_0 (.WBCLKI(wb_clk_i), .WBRSTI(wb_rst_i), .WBCYCI(wb_cyc_i),
+ .WBSTBI(wb_stb_i), .WBWEI(wb_we_i), .WBADRI7(wb_adr_i[7]), .WBADRI6(wb_adr_i[6]),
+ .WBADRI5(wb_adr_i[5]), .WBADRI4(wb_adr_i[4]), .WBADRI3(wb_adr_i[3]),
+ .WBADRI2(wb_adr_i[2]), .WBADRI1(wb_adr_i[1]), .WBADRI0(wb_adr_i[0]),
+ .WBDATI7(wb_dat_i[7]), .WBDATI6(wb_dat_i[6]), .WBDATI5(wb_dat_i[5]),
+ .WBDATI4(wb_dat_i[4]), .WBDATI3(wb_dat_i[3]), .WBDATI2(wb_dat_i[2]),
+ .WBDATI1(wb_dat_i[1]), .WBDATI0(wb_dat_i[0]), .PLL0DATI7(scuba_vlo),
+ .PLL0DATI6(scuba_vlo), .PLL0DATI5(scuba_vlo), .PLL0DATI4(scuba_vlo),
+ .PLL0DATI3(scuba_vlo), .PLL0DATI2(scuba_vlo), .PLL0DATI1(scuba_vlo),
+ .PLL0DATI0(scuba_vlo), .PLL0ACKI(scuba_vlo), .PLL1DATI7(scuba_vlo),
+ .PLL1DATI6(scuba_vlo), .PLL1DATI5(scuba_vlo), .PLL1DATI4(scuba_vlo),
+ .PLL1DATI3(scuba_vlo), .PLL1DATI2(scuba_vlo), .PLL1DATI1(scuba_vlo),
+ .PLL1DATI0(scuba_vlo), .PLL1ACKI(scuba_vlo), .I2C1SCLI(scuba_vlo),
+ .I2C1SDAI(scuba_vlo), .I2C2SCLI(scuba_vlo), .I2C2SDAI(scuba_vlo),
+ .SPISCKI(scuba_vlo), .SPIMISOI(scuba_vlo), .SPIMOSII(scuba_vlo),
+ .SPISCSN(scuba_vlo), .TCCLKI(scuba_vlo), .TCRSTN(scuba_vlo), .TCIC(scuba_vlo),
+ .UFMSN(scuba_vhi), .WBDATO7(wb_dat_o[7]), .WBDATO6(wb_dat_o[6]),
+ .WBDATO5(wb_dat_o[5]), .WBDATO4(wb_dat_o[4]), .WBDATO3(wb_dat_o[3]),
+ .WBDATO2(wb_dat_o[2]), .WBDATO1(wb_dat_o[1]), .WBDATO0(wb_dat_o[0]),
+ .WBACKO(wb_ack_o), .PLLCLKO(), .PLLRSTO(), .PLL0STBO(), .PLL1STBO(),
+ .PLLWEO(), .PLLADRO4(), .PLLADRO3(), .PLLADRO2(), .PLLADRO1(), .PLLADRO0(),
+ .PLLDATO7(), .PLLDATO6(), .PLLDATO5(), .PLLDATO4(), .PLLDATO3(),
+ .PLLDATO2(), .PLLDATO1(), .PLLDATO0(), .I2C1SCLO(), .I2C1SCLOEN(),
+ .I2C1SDAO(), .I2C1SDAOEN(), .I2C2SCLO(), .I2C2SCLOEN(), .I2C2SDAO(),
+ .I2C2SDAOEN(), .I2C1IRQO(), .I2C2IRQO(), .SPISCKO(), .SPISCKEN(),
+ .SPIMISOO(), .SPIMISOEN(), .SPIMOSIO(), .SPIMOSIEN(), .SPIMCSN7(),
+ .SPIMCSN6(), .SPIMCSN5(), .SPIMCSN4(), .SPIMCSN3(), .SPIMCSN2(),
+ .SPIMCSN1(), .SPIMCSN0(), .SPICSNEN(), .SPIIRQO(), .TCINT(), .TCOC(),
+ .WBCUFMIRQ(wbc_ufm_irq), .CFGWAKE(), .CFGSTDBY());
+
+
+
+ // exemplar begin
+ // exemplar end
+
+endmodule
diff --git a/fw/rtl/vendor/lcmxo2/vendor.sv b/fw/rtl/vendor/lcmxo2/vendor.sv
new file mode 100644
index 0000000..bf2ec4b
--- /dev/null
+++ b/fw/rtl/vendor/lcmxo2/vendor.sv
@@ -0,0 +1,127 @@
+module vendor (
+ input clk,
+ input reset,
+
+ vendor_scb.vendor vendor_scb
+);
+
+ logic start;
+ logic busy;
+ logic [1:0] length;
+ logic [5:0] delay;
+
+ logic request;
+ logic write;
+ logic ack;
+ logic [7:0] address;
+ logic [7:0] rdata;
+ logic [7:0] wdata;
+
+ logic [23:0] wdata_buffer;
+
+ logic ufm_irq;
+
+ always_comb begin
+ start = vendor_scb.control_valid && vendor_scb.control_wdata[0] && !busy;
+ vendor_scb.control_rdata = {
+ 16'd0,
+ address,
+ 4'b0000,
+ length,
+ write,
+ busy
+ };
+ end
+
+ always_ff @(posedge clk) begin
+ if (reset) begin
+ busy <= 1'b0;
+ end else begin
+ if (start) begin
+ busy <= 1'b1;
+ end
+ if (length == 2'd0 && ack) begin
+ busy <= 1'b0;
+ end
+ end
+ end
+
+ always_ff @(posedge clk) begin
+ if (start) begin
+ length <= vendor_scb.control_wdata[3:2];
+ end
+ if (ack && length > 2'd0) begin
+ length <= length - 1'd1;
+ end
+ end
+
+ always_ff @(posedge clk) begin
+ if (reset) begin
+ delay <= 6'd0;
+ end else begin
+ if (start && vendor_scb.control_wdata[4]) begin
+ delay <= 6'd35;
+ end
+ if (delay > 6'd0) begin
+ delay <= delay - 1'd1;
+ end
+ end
+ end
+
+ always_ff @(posedge clk) begin
+ if (reset) begin
+ request <= 1'b0;
+ end else begin
+ if (start) begin
+ request <= 1'b1;
+ end
+ if (busy && !request && delay == 6'd0) begin
+ request <= 1'b1;
+ end
+ if (ack) begin
+ request <= 1'b0;
+ end
+ end
+ end
+
+ always_ff @(posedge clk) begin
+ if (start) begin
+ write <= vendor_scb.control_wdata[1];
+ end
+ end
+
+ always_ff @(posedge clk) begin
+ if (start) begin
+ address <= vendor_scb.control_wdata[15:8];
+ end
+ end
+
+ always_ff @(posedge clk) begin
+ if (ack) begin
+ vendor_scb.data_rdata <= {vendor_scb.data_rdata[23:0], rdata};
+ end
+ end
+
+ always_ff @(posedge clk) begin
+ if (start) begin
+ {wdata, wdata_buffer} <= vendor_scb.data_wdata;
+ end
+ if (ack) begin
+ {wdata, wdata_buffer} <= {wdata_buffer, 8'h00};
+ end
+ end
+
+ efb_lattice_generated efb_lattice_generated_inst (
+ .wb_clk_i(clk),
+ .wb_rst_i(reset),
+ .wb_cyc_i(request),
+ .wb_stb_i(request),
+ .wb_we_i(write),
+ .wb_adr_i(address),
+ .wb_dat_i(wdata),
+ .wb_dat_o(rdata),
+ .wb_ack_o(ack),
+ .wbc_ufm_irq(ufm_irq)
+ );
+
+endmodule
diff --git a/fw/rtl/vendor/vendor_scb.sv b/fw/rtl/vendor/vendor_scb.sv
new file mode 100644
index 0000000..9c25b83
--- /dev/null
+++ b/fw/rtl/vendor/vendor_scb.sv
@@ -0,0 +1,25 @@
+interface vendor_scb ();
+
+ logic control_valid;
+ logic [31:0] control_rdata;
+ logic [31:0] control_wdata;
+ logic [31:0] data_rdata;
+ logic [31:0] data_wdata;
+
+ modport controller (
+ output control_valid,
+ input control_rdata,
+ output control_wdata,
+ input data_rdata,
+ output data_wdata
+ );
+
+ modport vendor (
+ input control_valid,
+ output control_rdata,
+ input control_wdata,
+ output data_rdata,
+ input data_wdata
+ );
+
+endinterface
diff --git a/sw/bootloader/src/sc64.c b/sw/bootloader/src/sc64.c
index 39bc53c..06ee528 100644
--- a/sw/bootloader/src/sc64.c
+++ b/sw/bootloader/src/sc64.c
@@ -93,7 +93,7 @@ bool sc64_usb_write_ready (void) {
return result[0];
}
-void sc64_usb_write (uint32_t *address, uint32_t length) {
+bool sc64_usb_write (uint32_t *address, uint32_t length) {
while (!sc64_usb_write_ready());
uint32_t args[2] = { (uint32_t) (address), length };
return sc64_execute_cmd(SC64_CMD_USB_WRITE, args, NULL);
diff --git a/sw/controller/Makefile b/sw/controller/Makefile
index 15c289e..040d7f8 100644
--- a/sw/controller/Makefile
+++ b/sw/controller/Makefile
@@ -24,6 +24,7 @@ SRC_FILES = \
gvr.c \
hw.c \
isv.c \
+ lcmxo2.c \
main.c \
rtc.c \
task.c \
diff --git a/sw/controller/src/fpga.h b/sw/controller/src/fpga.h
index ae9fa41..a2c8fad 100644
--- a/sw/controller/src/fpga.h
+++ b/sw/controller/src/fpga.h
@@ -50,7 +50,9 @@ typedef enum {
REG_DD_CMD_DATA,
REG_DD_HEAD_TRACK,
REG_DD_SECTOR_INFO,
- REG_DD_DRIVE_ID
+ REG_DD_DRIVE_ID,
+ REG_VENDOR_SCR,
+ REG_VENDOR_DATA
} fpga_reg_t;
diff --git a/sw/controller/src/lcmxo2.c b/sw/controller/src/lcmxo2.c
new file mode 100644
index 0000000..e0747e0
--- /dev/null
+++ b/sw/controller/src/lcmxo2.c
@@ -0,0 +1,232 @@
+#include
+#include "fpga.h"
+#include "vendor.h"
+
+
+#define VENDOR_SCR_START (1 << 0)
+#define VENDOR_SCR_BUSY (1 << 0)
+#define VENDOR_SCR_WRITE (1 << 1)
+#define VENDOR_SCR_LENGTH_BIT (2)
+#define VENDOR_SCR_DELAY (1 << 4)
+#define VENDOR_SCR_ADDRESS_BIT (8)
+
+#define LCMXO2_CFGCR (0x70)
+#define LCMXO2_CFGTXDR (0x71)
+#define LCMXO2_CFGRXDR (0x73)
+
+#define CFGCR_RSTE (1 << 6)
+#define CFGCR_WBCE (1 << 7)
+
+#define ISC_ERASE (0x0E)
+#define ISC_DISABLE (0x26)
+#define LSC_READ_STATUS (0x3C)
+#define LSC_INIT_ADDRESS (0x46)
+#define ISC_PROGRAM_DONE (0x5E)
+#define LSC_PROG_INCR_NV (0x70)
+#define LSC_READ_INCR_NV (0x73)
+#define ISC_ENABLE_X (0x74)
+#define LSC_REFRESH (0x79)
+#define ISC_NOOP (0xFF)
+
+#define ISC_ERASE_CFG (1 << 18)
+#define ISC_ERASE_UFM (1 << 19)
+
+#define LSC_STATUS_1_BUSY (1 << 4)
+#define LSC_STATUS_1_FAIL (1 << 5)
+
+#define FLASH_PAGE_SIZE (16)
+#define FLASH_NUM_PAGES (11260)
+
+
+typedef enum {
+ CMD_NORMAL,
+ CMD_DELAYED,
+ CMD_TWO_OP,
+} cmd_type_t;
+
+
+static void lcmxo2_reg_set (uint8_t reg, uint8_t value) {
+ fpga_reg_set(REG_VENDOR_DATA, value << 24);
+ fpga_reg_set(REG_VENDOR_SCR,
+ (reg << VENDOR_SCR_ADDRESS_BIT) |
+ (0 << VENDOR_SCR_LENGTH_BIT) |
+ VENDOR_SCR_WRITE |
+ VENDOR_SCR_START
+ );
+ while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY);
+}
+
+static uint8_t lcmxo2_reg_get (uint8_t reg) {
+ fpga_reg_set(REG_VENDOR_SCR,
+ (reg << VENDOR_SCR_ADDRESS_BIT) |
+ (0 << VENDOR_SCR_LENGTH_BIT) |
+ VENDOR_SCR_START
+ );
+ while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY);
+ return fpga_reg_get(REG_VENDOR_DATA) & 0xFF;
+}
+
+static void lcmxo2_reset_bus (void) {
+ lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_RSTE);
+ lcmxo2_reg_set(LCMXO2_CFGCR, 0);
+}
+
+static void lcmxo2_execute_cmd (uint8_t cmd, uint32_t arg, cmd_type_t type) {
+ lcmxo2_reg_set(LCMXO2_CFGCR, 0);
+ uint32_t data = (cmd << 24) | (arg & 0x00FFFFFF);
+ lcmxo2_reg_set(LCMXO2_CFGCR, CFGCR_WBCE);
+ fpga_reg_set(REG_VENDOR_DATA, data);
+ fpga_reg_set(REG_VENDOR_SCR,
+ (LCMXO2_CFGTXDR << VENDOR_SCR_ADDRESS_BIT) |
+ (type == CMD_DELAYED ? VENDOR_SCR_DELAY : 0) |
+ ((type == CMD_TWO_OP ? 2 : 3) << VENDOR_SCR_LENGTH_BIT) |
+ VENDOR_SCR_WRITE |
+ VENDOR_SCR_START
+ );
+ while (fpga_reg_get(REG_VENDOR_SCR) & VENDOR_SCR_BUSY);
+}
+
+static void lcmxo2_cleanup (void) {
+ lcmxo2_reg_set(LCMXO2_CFGCR, 0);
+}
+
+static void lcmxo2_read_data (uint8_t *buffer, uint32_t length) {
+ for (uint32_t i = 0; i < length; i++) {
+ *buffer++ = lcmxo2_reg_get(LCMXO2_CFGRXDR);
+ }
+}
+
+static void lcmxo2_write_data (uint8_t *buffer, uint32_t length) {
+ for (uint32_t i = 0; i < length; i++) {
+ lcmxo2_reg_set(LCMXO2_CFGTXDR, *buffer++);
+ }
+}
+
+static bool lcmxo2_wait_busy (void) {
+ uint8_t status[4];
+ do {
+ lcmxo2_execute_cmd(LSC_READ_STATUS, 0, CMD_NORMAL);
+ lcmxo2_read_data(status, 4);
+ } while(status[2] & LSC_STATUS_1_BUSY);
+ return status[2] & LSC_STATUS_1_FAIL;
+}
+
+static bool lcmxo2_enable_flash (void) {
+ lcmxo2_execute_cmd(ISC_ENABLE_X, 0x080000, CMD_NORMAL);
+ return lcmxo2_wait_busy();
+}
+
+static void lcmxo2_disable_flash (void) {
+ lcmxo2_wait_busy();
+ lcmxo2_execute_cmd(ISC_DISABLE, 0, CMD_TWO_OP);
+ lcmxo2_execute_cmd(ISC_NOOP, 0xFFFFFF, CMD_NORMAL);
+}
+
+static bool lcmxo2_erase_flash (void) {
+ lcmxo2_execute_cmd(ISC_ERASE, ISC_ERASE_UFM | ISC_ERASE_CFG, CMD_NORMAL);
+ return lcmxo2_wait_busy();
+}
+
+static void lcmxo2_reset_flash_address (void) {
+ lcmxo2_execute_cmd(LSC_INIT_ADDRESS, 0, CMD_NORMAL);
+}
+
+static bool lcmxo2_write_flash_page (uint8_t *buffer) {
+ lcmxo2_execute_cmd(LSC_PROG_INCR_NV, 1, CMD_NORMAL);
+ lcmxo2_write_data(buffer, FLASH_PAGE_SIZE);
+ return lcmxo2_wait_busy();
+}
+
+static void lcmxo2_read_flash_page (uint8_t *buffer) {
+ lcmxo2_execute_cmd(LSC_READ_INCR_NV, 1, CMD_DELAYED);
+ lcmxo2_read_data(buffer, FLASH_PAGE_SIZE);
+}
+
+static void lcmxo2_program_done (void) {
+ lcmxo2_execute_cmd(ISC_PROGRAM_DONE, 0, CMD_NORMAL);
+ lcmxo2_wait_busy();
+}
+
+static void lcmxo2_refresh (void) {
+ lcmxo2_execute_cmd(LSC_REFRESH, 0, CMD_TWO_OP);
+}
+
+static vendor_error_t lcmxo2_fail (vendor_error_t error) {
+ lcmxo2_disable_flash();
+ lcmxo2_cleanup();
+ return error;
+}
+
+vendor_error_t vendor_update (uint32_t address, uint32_t length) {
+ uint8_t buffer[FLASH_PAGE_SIZE];
+ uint8_t verify_buffer[FLASH_PAGE_SIZE];
+
+ if (length == 0) {
+ return VENDOR_ERROR_ARGS;
+ }
+ if ((length % FLASH_PAGE_SIZE) != 0) {
+ return VENDOR_ERROR_ARGS;
+ }
+ if (length > (FLASH_PAGE_SIZE * FLASH_NUM_PAGES)) {
+ return VENDOR_ERROR_ARGS;
+ }
+
+ lcmxo2_reset_bus();
+ if (lcmxo2_enable_flash()) {
+ return lcmxo2_fail(VENDOR_ERROR_INIT);
+ }
+ if (lcmxo2_erase_flash()) {
+ return lcmxo2_fail(VENDOR_ERROR_ERASE);
+ }
+ lcmxo2_reset_flash_address();
+ for (int i = 0; i < length; i += FLASH_PAGE_SIZE) {
+ fpga_mem_read(address + i, FLASH_PAGE_SIZE, buffer);
+ if (lcmxo2_write_flash_page(buffer)) {
+ return lcmxo2_fail(VENDOR_ERROR_PROGRAM);
+ }
+ }
+ lcmxo2_program_done();
+ lcmxo2_reset_flash_address();
+ for (int i = 0; i < length; i += FLASH_PAGE_SIZE) {
+ lcmxo2_read_flash_page(buffer);
+ fpga_mem_read(address + i, FLASH_PAGE_SIZE, verify_buffer);
+ for (int x = 0; x < FLASH_PAGE_SIZE; x++) {
+ if (buffer[x] != verify_buffer[x]) {
+ return lcmxo2_fail(VENDOR_ERROR_VERIFY);
+ }
+ }
+ }
+ lcmxo2_disable_flash();
+ lcmxo2_cleanup();
+
+ return VENDOR_OK;
+}
+
+vendor_error_t vendor_backup (uint32_t address, uint32_t *length) {
+ uint8_t buffer[FLASH_PAGE_SIZE];
+
+ *length = 0;
+
+ lcmxo2_reset_bus();
+ if (lcmxo2_enable_flash()) {
+ return lcmxo2_fail(VENDOR_ERROR_INIT);
+ }
+ lcmxo2_reset_flash_address();
+ for (int i = 0; i < (FLASH_PAGE_SIZE * FLASH_NUM_PAGES); i += FLASH_PAGE_SIZE) {
+ lcmxo2_read_flash_page(buffer);
+ fpga_mem_write(address + i, FLASH_PAGE_SIZE, buffer);
+ *length += FLASH_PAGE_SIZE;
+ }
+ lcmxo2_disable_flash();
+ lcmxo2_cleanup();
+
+ return VENDOR_OK;
+}
+
+vendor_error_t vendor_reconfigure (void) {
+ lcmxo2_reset_bus();
+ lcmxo2_refresh();
+ lcmxo2_cleanup();
+
+ return VENDOR_OK;
+}
diff --git a/sw/controller/src/usb.c b/sw/controller/src/usb.c
index 6db94dd..5db9508 100644
--- a/sw/controller/src/usb.c
+++ b/sw/controller/src/usb.c
@@ -1,12 +1,13 @@
#include
#include
+#include "cfg.h"
#include "cic.h"
#include "dd.h"
-#include "cfg.h"
+#include "flash.h"
#include "fpga.h"
#include "rtc.h"
#include "usb.h"
-#include "flash.h"
+#include "vendor.h"
enum rx_state {
@@ -249,12 +250,33 @@ static void usb_rx_process (void) {
}
break;
+ case 'f':
+ p.response_info.data[0] = vendor_backup(p.rx_args[0], &p.response_info.data[1]);
+ p.rx_state = RX_STATE_IDLE;
+ p.response_pending = true;
+ p.response_info.data_length = 8;
+ if (p.response_info.data[0] != VENDOR_OK) {
+ p.response_error = true;
+ }
+ break;
+
+ case 'F':
+ p.response_info.data[0] = vendor_update(p.rx_args[0], p.rx_args[1]);
+ p.rx_state = RX_STATE_IDLE;
+ p.response_pending = true;
+ p.response_info.data_length = 4;
+ if (p.response_info.data[0] != VENDOR_OK) {
+ p.response_error = true;
+ }
+ break;
+
default:
p.rx_state = RX_STATE_IDLE;
p.response_pending = true;
p.response_error = true;
p.response_info.data_length = 4;
p.response_info.data[0] = 0xFF;
+ break;
}
}
}
diff --git a/sw/controller/src/vendor.h b/sw/controller/src/vendor.h
new file mode 100644
index 0000000..984954e
--- /dev/null
+++ b/sw/controller/src/vendor.h
@@ -0,0 +1,23 @@
+#ifndef VENDOR_H__
+#define VENDOR_H__
+
+
+#include
+
+
+typedef enum {
+ VENDOR_OK,
+ VENDOR_ERROR_ARGS,
+ VENDOR_ERROR_INIT,
+ VENDOR_ERROR_ERASE,
+ VENDOR_ERROR_PROGRAM,
+ VENDOR_ERROR_VERIFY,
+} vendor_error_t;
+
+
+vendor_error_t vendor_update (uint32_t address, uint32_t length);
+vendor_error_t vendor_backup (uint32_t address, uint32_t *length);
+vendor_error_t vendor_reconfigure (void);
+
+
+#endif