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