diff --git a/firmware/programmer/Makefile.linux b/firmware/programmer/Makefile.linux index 401ab43..34b7cae 100644 --- a/firmware/programmer/Makefile.linux +++ b/firmware/programmer/Makefile.linux @@ -9,7 +9,7 @@ USB_DIR=../usb_cdc/ OBJ_DIR=obj/ LIB_DIR=../libs/ INCLUDE_DIR=../ -TOOLCHAIN=../../../compiler/gcc-arm-none-eabi-4_9-2015q1/bin/arm-none-eabi- +TOOLCHAIN=arm-none-eabi- APP_1=$(OBJ_DIR)$(APP_NAME)_1 APP_2=$(OBJ_DIR)$(APP_NAME)_2 @@ -54,8 +54,8 @@ USB_SRCS=hw_config.c stm32_it.c usb_prop.c usb_desc.c usb_istr.c usb_pwr.c \ usb_endp.c usb.c SRCS=main.c system_stm32f10x.c syscalls.c fsmc_nand.c led.c uart.c jtag.c \ - clock.c cdc.c nand_programmer.c nand_bad_block.c flash.c spi_flash.c \ - $(USB_SRCS) + clock.c cdc.c nand_programmer.c nand_bad_block.c flash.c spi_nor_flash.c \ + spi_nand_flash.c $(USB_SRCS) OBJS=$(addprefix $(OBJ_DIR),$(SRCS:.c=.o)) \ $(addprefix $(OBJ_DIR),$(STARTUP:.s=.o)) diff --git a/firmware/programmer/Makefile.windows b/firmware/programmer/Makefile.windows index 6d464d7..d411e50 100644 --- a/firmware/programmer/Makefile.windows +++ b/firmware/programmer/Makefile.windows @@ -54,8 +54,8 @@ USB_SRCS=hw_config.c stm32_it.c usb_prop.c usb_desc.c usb_istr.c usb_pwr.c \ usb_endp.c usb.c SRCS=main.c system_stm32f10x.c syscalls.c fsmc_nand.c led.c uart.c jtag.c \ - clock.c cdc.c nand_programmer.c nand_bad_block.c flash.c spi_flash.c \ - $(USB_SRCS) + clock.c cdc.c nand_programmer.c nand_bad_block.c flash.c spi_nor_flash.c \ + spi_nand_flash.c $(USB_SRCS) OBJS=$(addprefix $(OBJ_DIR)/,$(SRCS:.c=.o)) \ $(addprefix $(OBJ_DIR)/,$(STARTUP:.s=.o)) diff --git a/firmware/programmer/chip.h b/firmware/programmer/chip.h index a6af421..6c5bbd0 100644 --- a/firmware/programmer/chip.h +++ b/firmware/programmer/chip.h @@ -6,13 +6,14 @@ #ifndef _CHIP_H_ #define _CHIP_H_ -typedef struct __attribute__((__packed__)) +typedef struct { uint8_t maker_id; uint8_t device_id; uint8_t third_id; uint8_t fourth_id; uint8_t fifth_id; + uint8_t sixth_id; } chip_id_t; #endif /* _CHIP_H_ */ diff --git a/firmware/programmer/fsmc_nand.c b/firmware/programmer/fsmc_nand.c index 625a2f0..afbd79f 100644 --- a/firmware/programmer/fsmc_nand.c +++ b/firmware/programmer/fsmc_nand.c @@ -223,6 +223,7 @@ static void nand_read_id(chip_id_t *nand_id) data = *((__IO uint32_t *)(Bank_NAND_ADDR | DATA_AREA) + 1); nand_id->fifth_id = ADDR_1st_CYCLE(data); + nand_id->sixth_id = ADDR_2nd_CYCLE(data); } static void nand_write_page_async(uint8_t *buf, uint32_t page, @@ -510,4 +511,3 @@ flash_hal_t hal_fsmc = .is_bb_supported = nand_is_bb_supported, .enable_hw_ecc = nand_enable_hw_ecc, }; - diff --git a/firmware/programmer/nand_programmer.c b/firmware/programmer/nand_programmer.c index b5d65f0..2b3dce4 100644 --- a/firmware/programmer/nand_programmer.c +++ b/firmware/programmer/nand_programmer.c @@ -11,7 +11,8 @@ #include "log.h" #include "version.h" #include "flash.h" -#include "spi_flash.h" +#include "spi_nor_flash.h" +#include "spi_nand_flash.h" #include #include #include @@ -19,7 +20,7 @@ #include #define NP_PACKET_BUF_SIZE 64 -#define NP_MAX_PAGE_SIZE 0x21C0 /* 8KB + 448 spare */ +#define NP_MAX_PAGE_SIZE 0x22E8 /* 8KB + 744 spare */ #define NP_WRITE_ACK_BYTES 1984 #define NP_NAND_TIMEOUT 0x1000000 @@ -157,8 +158,6 @@ typedef struct __attribute__((__packed__)) chip_id_t nand_id; } np_resp_id_t; -/* BB, write ack and error responses are aligned to the same size to avoid - * receiver wait for additional data */ typedef struct __attribute__((__packed__)) { np_resp_t header; @@ -170,14 +169,12 @@ typedef struct __attribute__((__packed__)) { np_resp_t header; uint64_t bytes_ack; - uint8_t dummy[4]; } np_resp_write_ack_t; typedef struct __attribute__((__packed__)) { np_resp_t header; uint8_t err_code; - uint8_t dummy[11]; } np_resp_err_t; typedef struct __attribute__((__packed__)) @@ -257,7 +254,7 @@ typedef struct static np_comm_cb_t *np_comm_cb; static np_prog_t prog; -static flash_hal_t *hal[] = { &hal_fsmc, &hal_spi }; +static flash_hal_t *hal[] = { &hal_fsmc, &hal_spi_nor, &hal_spi_nand }; uint8_t np_packet_send_buf[NP_PACKET_BUF_SIZE]; @@ -321,9 +318,9 @@ static int _np_cmd_nand_read_id(np_prog_t *prog) if (np_comm_cb) np_comm_cb->send((uint8_t *)&resp, resp_len); - DEBUG_PRINT("Chip ID: 0x%x 0x%x 0x%x 0x%x 0x%x\r\n", + DEBUG_PRINT("Chip ID: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\r\n", resp.nand_id.maker_id, resp.nand_id.device_id, resp.nand_id.third_id, - resp.nand_id.fourth_id, resp.nand_id.fifth_id); + resp.nand_id.fourth_id, resp.nand_id.fifth_id, resp.nand_id.sixth_id); return 0; } diff --git a/firmware/programmer/spi_flash.c b/firmware/programmer/spi_flash.c index dce906b..b101264 100644 --- a/firmware/programmer/spi_flash.c +++ b/firmware/programmer/spi_flash.c @@ -240,6 +240,8 @@ static void spi_flash_read_id(chip_id_t *chip_id) chip_id->device_id = spi_flash_read_byte(); chip_id->third_id = spi_flash_read_byte(); chip_id->fourth_id = spi_flash_read_byte(); + chip_id->fifth_id = spi_flash_read_byte(); + chip_id->sixth_id = spi_flash_read_byte(); spi_flash_deselect_chip(); } diff --git a/firmware/programmer/spi_nand_flash.c b/firmware/programmer/spi_nand_flash.c new file mode 100644 index 0000000..665689b --- /dev/null +++ b/firmware/programmer/spi_nand_flash.c @@ -0,0 +1,554 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nand_flash.h" +#include + +/* SPI NAND Command Set */ +#define _SPI_NAND_OP_GET_FEATURE 0x0F /* Get Feature */ +#define _SPI_NAND_OP_SET_FEATURE 0x1F /* Set Feature */ +#define _SPI_NAND_OP_PAGE_READ 0x13 /* Load page data into cache of SPI NAND chip */ +#define _SPI_NAND_OP_READ_FROM_CACHE_SINGLE 0x03 /* Read data from cache of SPI NAND chip, single speed*/ +#define _SPI_NAND_OP_READ_FROM_CACHE_DUAL 0x3B /* Read data from cache of SPI NAND chip, dual speed*/ +#define _SPI_NAND_OP_READ_FROM_CACHE_QUAD 0x6B /* Read data from cache of SPI NAND chip, quad speed*/ +#define _SPI_NAND_OP_WRITE_ENABLE 0x06 /* Enable write data to SPI NAND chip */ +#define _SPI_NAND_OP_WRITE_DISABLE 0x04 /* Reseting the Write Enable Latch (WEL) */ +#define _SPI_NAND_OP_PROGRAM_LOAD_SINGLE 0x02 /* Write data into cache of SPI NAND chip with cache reset, single speed */ +#define _SPI_NAND_OP_PROGRAM_LOAD_QUAD 0x32 /* Write data into cache of SPI NAND chip with cache reset, quad speed */ +#define _SPI_NAND_OP_PROGRAM_LOAD_RAMDOM_SINGLE 0x84 /* Write data into cache of SPI NAND chip, single speed */ +#define _SPI_NAND_OP_PROGRAM_LOAD_RAMDON_QUAD 0x34 /* Write data into cache of SPI NAND chip, quad speed */ + +#define _SPI_NAND_OP_PROGRAM_EXECUTE 0x10 /* Write data from cache into SPI NAND chip */ +#define _SPI_NAND_OP_READ_ID 0x9F /* Read Manufacture ID and Device ID */ +#define _SPI_NAND_OP_BLOCK_ERASE 0xD8 /* Erase Block */ +#define _SPI_NAND_OP_RESET 0xFF /* Reset */ +#define _SPI_NAND_OP_DIE_SELECT 0xC2 /* Die Select */ + +/* SPI NAND register address of command set */ +#define _SPI_NAND_ADDR_ECC 0x90 /* Address of ECC Config */ +#define _SPI_NAND_ADDR_PROTECT 0xA0 /* Address of protection */ +#define _SPI_NAND_ADDR_FEATURE 0xB0 /* Address of feature */ +#define _SPI_NAND_ADDR_STATUS 0xC0 /* Address of status */ +#define _SPI_NAND_ADDR_FEATURE_4 0xD0 /* Address of status 4 */ +#define _SPI_NAND_ADDR_STATUS_5 0xE0 /* Address of status 5 */ +#define _SPI_NAND_ADDR_MANUFACTURE_ID 0x00 /* Address of Manufacture ID */ +#define _SPI_NAND_ADDR_DEVICE_ID 0x01 /* Address of Device ID */ + +/* SPI NAND value of register address of command set */ +#define _SPI_NAND_VAL_DISABLE_PROTECTION 0x0 /* Value for disable write protection */ +#define _SPI_NAND_VAL_ENABLE_PROTECTION 0x38 /* Value for enable write protection */ +#define _SPI_NAND_VAL_OIP 0x1 /* OIP = Operaton In Progress */ +#define _SPI_NAND_VAL_ERASE_FAIL 0x4 /* E_FAIL = Erase Fail */ +#define _SPI_NAND_VAL_PROGRAM_FAIL 0x8 /* P_FAIL = Program Fail */ + + +#define SPI_FLASH_CS_PIN GPIO_Pin_4 +#define SPI_FLASH_SCK_PIN GPIO_Pin_5 +#define SPI_FLASH_MISO_PIN GPIO_Pin_6 +#define SPI_FLASH_MOSI_PIN GPIO_Pin_7 + +#define FLASH_DUMMY_BYTE 0xFF + +/* 1st addressing cycle */ +#define ADDR_1st_CYCLE(ADDR) (uint8_t)((ADDR)& 0xFF) +/* 2nd addressing cycle */ +#define ADDR_2nd_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF00) >> 8) +/* 3rd addressing cycle */ +#define ADDR_3rd_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF0000) >> 16) +/* 4th addressing cycle */ +#define ADDR_4th_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF000000) >> 24) + +#define UNDEFINED_CMD 0xFF + +static void spi_flash_chip_init(void); + +typedef struct __attribute__((__packed__)) +{ + uint32_t spare_offset; + uint8_t mode_data; + uint8_t unlock_data; + uint8_t ecc_err_bits_mask; + uint8_t ecc_err_bits_state; + uint8_t read_dummy_prepend; + uint8_t plane_select_have; + uint8_t die_select_type; + uint32_t freq; +} spi_conf_t; + +static spi_conf_t spi_conf; + +enum +{ + FLASH_OP_EMPTY = 0, + FLASH_OP_ERASE = 1, + FLASH_OP_WRITE = 2, + FLASH_OP_READ = 3, + FLASH_OP_SPARE = 4, +}; + +static uint32_t flash_last_operation = FLASH_OP_EMPTY; +static uint32_t current_die = 0; + +static void spi_flash_gpio_init() +{ + GPIO_InitTypeDef gpio_init; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + + /* Enable SPI peripheral clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); + + /* Configure SPI SCK pin */ + gpio_init.GPIO_Pin = SPI_FLASH_SCK_PIN; + gpio_init.GPIO_Speed = GPIO_Speed_50MHz; + gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; + GPIO_Init(GPIOA, &gpio_init); + + /* Configure SPI MOSI pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MOSI_PIN; + GPIO_Init(GPIOA, &gpio_init); + + /* Configure SPI MISO pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MISO_PIN; + gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOA, &gpio_init); + + /* Configure SPI CS pin */ + gpio_init.GPIO_Pin = SPI_FLASH_CS_PIN; + gpio_init.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOA, &gpio_init); +} + +static void spi_flash_gpio_uninit() +{ + GPIO_InitTypeDef gpio_init; + + /* Disable SPI peripheral clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE); + + /* Disable SPI SCK pin */ + gpio_init.GPIO_Pin = SPI_FLASH_SCK_PIN; + gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOA, &gpio_init); + + /* Disable SPI MISO pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MISO_PIN; + GPIO_Init(GPIOA, &gpio_init); + + /* Disable SPI MOSI pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MOSI_PIN; + GPIO_Init(GPIOA, &gpio_init); + + /* Disable SPI CS pin */ + gpio_init.GPIO_Pin = SPI_FLASH_CS_PIN; + GPIO_Init(GPIOA, &gpio_init); +} + +static inline void spi_flash_select_chip() +{ + GPIO_ResetBits(GPIOA, SPI_FLASH_CS_PIN); +} + +static inline void spi_flash_deselect_chip() +{ + GPIO_SetBits(GPIOA, SPI_FLASH_CS_PIN); +} + +static uint16_t spi_flash_get_baud_rate_prescaler(uint32_t spi_freq_khz) +{ + uint32_t system_clock_khz = SystemCoreClock / 1000; + + if (spi_freq_khz >= system_clock_khz / 2) + return SPI_BaudRatePrescaler_2; + else if (spi_freq_khz >= system_clock_khz / 4) + return SPI_BaudRatePrescaler_4; + else if (spi_freq_khz >= system_clock_khz / 8) + return SPI_BaudRatePrescaler_8; + else if (spi_freq_khz >= system_clock_khz / 16) + return SPI_BaudRatePrescaler_16; + else if (spi_freq_khz >= system_clock_khz / 32) + return SPI_BaudRatePrescaler_32; + else if (spi_freq_khz >= system_clock_khz / 64) + return SPI_BaudRatePrescaler_64; + else if (spi_freq_khz >= system_clock_khz / 128) + return SPI_BaudRatePrescaler_128; + else + return SPI_BaudRatePrescaler_256; +} + +static int spi_flash_init(void *conf, uint32_t conf_size) +{ + SPI_InitTypeDef spi_init; + + if (conf_size < sizeof(spi_conf_t)) + return -1; + spi_conf = *(spi_conf_t *)conf; + + spi_flash_gpio_init(); + + spi_flash_deselect_chip(); + + /* Configure SPI */ + spi_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + spi_init.SPI_Mode = SPI_Mode_Master; + spi_init.SPI_DataSize = SPI_DataSize_8b; + spi_init.SPI_CPOL = SPI_CPOL_High; + spi_init.SPI_CPHA = SPI_CPHA_2Edge; + spi_init.SPI_NSS = SPI_NSS_Soft; + spi_init.SPI_BaudRatePrescaler = + spi_flash_get_baud_rate_prescaler(spi_conf.freq); + spi_init.SPI_FirstBit = SPI_FirstBit_MSB; + spi_init.SPI_CRCPolynomial = 7; + SPI_Init(SPI1, &spi_init); + + /* Enable SPI */ + SPI_Cmd(SPI1, ENABLE); + + spi_flash_chip_init(); + + return 0; +} + +static void spi_flash_uninit() +{ + spi_flash_gpio_uninit(); + + /* Disable SPI */ + SPI_Cmd(SPI3, DISABLE); +} + +static uint8_t spi_flash_send_byte(uint8_t byte) +{ + /* Loop while DR register in not emplty */ + while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); + + /* Send byte through the SPI1 peripheral to generate clock signal */ + SPI_I2S_SendData(SPI1, byte); + + /* Wait to receive a byte */ + while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); + + /* Return the byte read from the SPI bus */ + return SPI_I2S_ReceiveData(SPI1); +} + +static inline uint8_t spi_flash_read_byte() +{ + return spi_flash_send_byte(FLASH_DUMMY_BYTE); +} + +static void spi_flash_set_feature(uint8_t addr, uint8_t data) +{ + spi_flash_select_chip(); + spi_flash_send_byte(_SPI_NAND_OP_SET_FEATURE); + spi_flash_send_byte(addr); + spi_flash_send_byte(data); + spi_flash_deselect_chip(); +} + +static void spi_flash_get_feature(uint8_t addr, uint8_t *data) +{ + spi_flash_select_chip(); + spi_flash_send_byte(_SPI_NAND_OP_GET_FEATURE); + spi_flash_send_byte(addr); + *data = spi_flash_read_byte(); + spi_flash_deselect_chip(); +} + +static uint32_t spi_flash_read_status() +{ + uint32_t timeout = 0x1000000;//TODO + uint8_t status; + + do { + spi_flash_get_feature(_SPI_NAND_ADDR_STATUS, &status); + } while((status & _SPI_NAND_VAL_OIP) && timeout) ; + + if (!timeout) + return FLASH_STATUS_TIMEOUT; + + switch(flash_last_operation){ + case FLASH_OP_ERASE: + if(status & _SPI_NAND_VAL_ERASE_FAIL) + return FLASH_STATUS_ERROR; + break; + case FLASH_OP_WRITE: + if(status & _SPI_NAND_VAL_PROGRAM_FAIL) + return FLASH_STATUS_ERROR; + break; + case FLASH_OP_READ: + if((status & spi_conf.ecc_err_bits_mask) == spi_conf.ecc_err_bits_state) + return FLASH_STATUS_ERROR; + break; + case FLASH_OP_SPARE: + case FLASH_OP_EMPTY: + default: + break; + } + return FLASH_STATUS_READY; +} + +static void spi_flash_select_die_cmd(uint32_t die) +{ + switch(spi_conf.die_select_type) { + case 1: { + spi_flash_select_chip(); + spi_flash_send_byte(_SPI_NAND_OP_DIE_SELECT); + spi_flash_send_byte(die); + spi_flash_deselect_chip(); + break; + } + case 2: { + uint8_t feature; + spi_flash_get_feature(_SPI_NAND_ADDR_FEATURE_4, &feature); + if(die == 0) { + feature &= ~(0x40); + } else { + feature |= 0x40; + } + spi_flash_set_feature(_SPI_NAND_ADDR_FEATURE_4, feature); + break; + } + default: + break; + } +} + +static void spi_flash_select_die(uint32_t page) +{ + uint32_t die = 0; + if(spi_conf.die_select_type) { + if(!spi_conf.plane_select_have) + die = ((page >> 16) & 0xff); + else + die = ((page >> 17) & 0xff); + if (current_die != die) { + current_die = die; + spi_flash_select_die_cmd(die); + } + } +} + +static void spi_flash_read_id(chip_id_t *chip_id) +{ + spi_flash_select_chip(); + + spi_flash_send_byte(_SPI_NAND_OP_READ_ID); + spi_flash_send_byte(_SPI_NAND_ADDR_MANUFACTURE_ID); + + chip_id->maker_id = spi_flash_read_byte(); + chip_id->device_id = spi_flash_read_byte(); + chip_id->third_id = spi_flash_read_byte(); + chip_id->fourth_id = spi_flash_read_byte(); + chip_id->fifth_id = spi_flash_read_byte(); + chip_id->sixth_id = spi_flash_read_byte(); + + spi_flash_deselect_chip(); +} + +static void spi_flash_chip_init(void) +{ + if(spi_conf.die_select_type) { + spi_flash_select_die_cmd(0); + if(spi_conf.mode_data != UNDEFINED_CMD) + spi_flash_set_feature(_SPI_NAND_ADDR_FEATURE, spi_conf.mode_data); + if(spi_conf.unlock_data != UNDEFINED_CMD) + spi_flash_set_feature(_SPI_NAND_ADDR_PROTECT, spi_conf.unlock_data); + spi_flash_select_die_cmd(1); + } + if(spi_conf.mode_data != UNDEFINED_CMD) + spi_flash_set_feature(_SPI_NAND_ADDR_FEATURE, spi_conf.mode_data); + if(spi_conf.unlock_data != UNDEFINED_CMD) + spi_flash_set_feature(_SPI_NAND_ADDR_PROTECT, spi_conf.unlock_data); +} + +static void spi_flash_write_enable() +{ + spi_flash_select_chip(); + spi_flash_send_byte(_SPI_NAND_OP_WRITE_ENABLE); + spi_flash_deselect_chip(); +} + +//static void spi_flash_write_disable() +//{ +// spi_flash_select_chip(); +// spi_flash_send_byte(_SPI_NAND_OP_WRITE_DISABLE); +// spi_flash_deselect_chip(); +//} + +static void spi_flash_program_load(uint8_t *buf, uint32_t page_size, uint32_t page) +{ + uint32_t i; + uint32_t addr = 0; + spi_flash_select_chip(); + + spi_flash_send_byte(_SPI_NAND_OP_PROGRAM_LOAD_SINGLE); + + if(spi_conf.plane_select_have) { + if((page >> 6)& (0x1)) + spi_flash_send_byte(ADDR_2nd_CYCLE(addr) | (0x10)); + else + spi_flash_send_byte(ADDR_2nd_CYCLE(addr) & (0xef)); + } else { + spi_flash_send_byte(ADDR_2nd_CYCLE(addr)); + } + + spi_flash_send_byte(ADDR_1st_CYCLE(addr)); + + for (i = 0; i < page_size; i++) + spi_flash_send_byte(buf[i]); + + spi_flash_deselect_chip(); +} + +static void spi_flash_write_page_async(uint8_t *buf, uint32_t page, + uint32_t page_size) +{ + spi_flash_select_die(page); + + spi_flash_program_load(buf, page_size, page); + + spi_flash_write_enable(); + + spi_flash_select_chip(); + spi_flash_send_byte(_SPI_NAND_OP_PROGRAM_EXECUTE); + flash_last_operation = FLASH_OP_WRITE; + spi_flash_send_byte(ADDR_3rd_CYCLE(page)); + spi_flash_send_byte(ADDR_2nd_CYCLE(page)); + spi_flash_send_byte(ADDR_1st_CYCLE(page)); + spi_flash_deselect_chip(); +// spi_flash_wait_operation_end(); + +// spi_flash_write_disable(); +} + +static uint32_t spi_flash_load_page_into_cache(uint32_t page) +{ + spi_flash_select_die(page); + + spi_flash_select_chip(); + spi_flash_send_byte(_SPI_NAND_OP_PAGE_READ); + flash_last_operation = FLASH_OP_READ; + spi_flash_send_byte(ADDR_3rd_CYCLE(page)); + spi_flash_send_byte(ADDR_2nd_CYCLE(page)); + spi_flash_send_byte(ADDR_1st_CYCLE(page)); + spi_flash_deselect_chip(); + + return spi_flash_read_status(); +} + +static uint32_t spi_flash_read_page(uint8_t *buf, uint32_t page, uint32_t data_size) +{ + uint32_t status = spi_flash_load_page_into_cache(page); + uint32_t data_offset = 0; + + spi_flash_select_chip(); + spi_flash_send_byte(_SPI_NAND_OP_READ_FROM_CACHE_SINGLE); + + if(spi_conf.read_dummy_prepend) + spi_flash_send_byte(FLASH_DUMMY_BYTE); + + if(spi_conf.plane_select_have) { + if((page >> 6)& (0x1)) + spi_flash_send_byte(ADDR_2nd_CYCLE(data_offset) | (0x10)); + else + spi_flash_send_byte(ADDR_2nd_CYCLE(data_offset) & (0xef)); + } else { + spi_flash_send_byte(ADDR_2nd_CYCLE(data_offset)); + } + + spi_flash_send_byte(ADDR_1st_CYCLE(data_offset)); + + if(!spi_conf.read_dummy_prepend) + spi_flash_send_byte(FLASH_DUMMY_BYTE); + + for(uint32_t i = 0; i < data_size; i++) + buf[i] = spi_flash_read_byte(); + + spi_flash_deselect_chip(); + return status; +} + +static uint32_t spi_flash_read_spare_data(uint8_t *buf, uint32_t page, + uint32_t offset, uint32_t data_size) +{ + uint32_t status; + + spi_flash_select_die(page); + + spi_flash_select_chip(); + spi_flash_send_byte(_SPI_NAND_OP_PAGE_READ); + flash_last_operation = FLASH_OP_SPARE; + spi_flash_send_byte(ADDR_3rd_CYCLE(page)); + spi_flash_send_byte(ADDR_2nd_CYCLE(page)); + spi_flash_send_byte(ADDR_1st_CYCLE(page)); + spi_flash_deselect_chip(); + status = spi_flash_read_status(); + + spi_flash_select_chip(); + spi_flash_send_byte(_SPI_NAND_OP_READ_FROM_CACHE_SINGLE); + + if(spi_conf.read_dummy_prepend) + spi_flash_send_byte(FLASH_DUMMY_BYTE); + + offset += spi_conf.spare_offset; + if(spi_conf.plane_select_have) { + if((page >> 6)& (0x1)) + spi_flash_send_byte(ADDR_2nd_CYCLE(offset) | (0x10)); + else + spi_flash_send_byte(ADDR_2nd_CYCLE(offset) & (0xef)); + } else { + spi_flash_send_byte(ADDR_2nd_CYCLE(offset)); + } + spi_flash_send_byte(ADDR_1st_CYCLE(offset)); + + if(!spi_conf.read_dummy_prepend) + spi_flash_send_byte(FLASH_DUMMY_BYTE); + + for(uint32_t i = 0; i < data_size; i++) + buf[i] = spi_flash_read_byte(); + + spi_flash_deselect_chip(); + return status; +} + +static uint32_t spi_flash_erase_block(uint32_t page) +{ + spi_flash_select_die(page); + + spi_flash_write_enable(); + + spi_flash_select_chip(); + + spi_flash_send_byte(_SPI_NAND_OP_BLOCK_ERASE); + flash_last_operation = FLASH_OP_ERASE; + + spi_flash_send_byte(ADDR_3rd_CYCLE(page)); + spi_flash_send_byte(ADDR_2nd_CYCLE(page)); + spi_flash_send_byte(ADDR_1st_CYCLE(page)); + + spi_flash_deselect_chip(); + + return spi_flash_read_status(); +} + +static inline bool spi_flash_is_bb_supported() +{ + return true; +} + + +flash_hal_t hal_spi_nand = +{ + .init = spi_flash_init, + .uninit = spi_flash_uninit, + .read_id = spi_flash_read_id, + .erase_block = spi_flash_erase_block, + .read_page = spi_flash_read_page, + .read_spare_data = spi_flash_read_spare_data, + .write_page_async = spi_flash_write_page_async, + .read_status = spi_flash_read_status, + .is_bb_supported = spi_flash_is_bb_supported +}; diff --git a/firmware/programmer/spi_nand_flash.h b/firmware/programmer/spi_nand_flash.h new file mode 100644 index 0000000..4cfc925 --- /dev/null +++ b/firmware/programmer/spi_nand_flash.h @@ -0,0 +1,13 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef _SPI_NAND_FLASH_H_ +#define _SPI_NAND_FLASH_H_ + +#include "flash_hal.h" + +extern flash_hal_t hal_spi_nand; + +#endif /* _SPI_NAND_FLASH_H_ */ diff --git a/firmware/programmer/spi_nor_flash.c b/firmware/programmer/spi_nor_flash.c new file mode 100644 index 0000000..afd1fa9 --- /dev/null +++ b/firmware/programmer/spi_nor_flash.c @@ -0,0 +1,374 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_flash.h" +#include + +#define SPI_FLASH_CS_PIN GPIO_Pin_4 +#define SPI_FLASH_SCK_PIN GPIO_Pin_5 +#define SPI_FLASH_MISO_PIN GPIO_Pin_6 +#define SPI_FLASH_MOSI_PIN GPIO_Pin_7 + +#define FLASH_DUMMY_BYTE 0xA5 + +#define FLASH_READY 0 +#define FLASH_BUSY 1 +#define FLASH_TIMEOUT 2 + +/* 1st addressing cycle */ +#define ADDR_1st_CYCLE(ADDR) (uint8_t)((ADDR)& 0xFF) +/* 2nd addressing cycle */ +#define ADDR_2nd_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF00) >> 8) +/* 3rd addressing cycle */ +#define ADDR_3rd_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF0000) >> 16) +/* 4th addressing cycle */ +#define ADDR_4th_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF000000) >> 24) + +#define UNDEFINED_CMD 0xFF + +typedef struct __attribute__((__packed__)) +{ + uint8_t page_offset; + uint8_t read_cmd; + uint8_t read_id_cmd; + uint8_t write_cmd; + uint8_t write_en_cmd; + uint8_t erase_cmd; + uint8_t status_cmd; + uint8_t busy_bit; + uint8_t busy_state; + uint32_t freq; + uint8_t addr_bytes; // 地址字节数:3或4 + uint8_t enter_4byte_cmd; // 进入4字节模式命令 + uint8_t exit_4byte_cmd; // 退出4字节模式命令 +} spi_conf_t; + +static spi_conf_t spi_conf; + +static void spi_flash_gpio_init() +{ + GPIO_InitTypeDef gpio_init; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + + /* Enable SPI peripheral clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); + + /* Configure SPI SCK pin */ + gpio_init.GPIO_Pin = SPI_FLASH_SCK_PIN; + gpio_init.GPIO_Speed = GPIO_Speed_50MHz; + gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; + GPIO_Init(GPIOA, &gpio_init); + + /* Configure SPI MOSI pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MOSI_PIN; + GPIO_Init(GPIOA, &gpio_init); + + /* Configure SPI MISO pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MISO_PIN; + gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOA, &gpio_init); + + /* Configure SPI CS pin */ + gpio_init.GPIO_Pin = SPI_FLASH_CS_PIN; + gpio_init.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOA, &gpio_init); +} + +static void spi_flash_gpio_uninit() +{ + GPIO_InitTypeDef gpio_init; + + /* Disable SPI peripheral clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE); + + /* Disable SPI SCK pin */ + gpio_init.GPIO_Pin = SPI_FLASH_SCK_PIN; + gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOA, &gpio_init); + + /* Disable SPI MISO pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MISO_PIN; + GPIO_Init(GPIOA, &gpio_init); + + /* Disable SPI MOSI pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MOSI_PIN; + GPIO_Init(GPIOA, &gpio_init); + + /* Disable SPI CS pin */ + gpio_init.GPIO_Pin = SPI_FLASH_CS_PIN; + GPIO_Init(GPIOA, &gpio_init); +} + +static inline void spi_flash_select_chip() +{ + GPIO_ResetBits(GPIOA, SPI_FLASH_CS_PIN); +} + +static inline void spi_flash_deselect_chip() +{ + GPIO_SetBits(GPIOA, SPI_FLASH_CS_PIN); +} + +static uint16_t spi_flash_get_baud_rate_prescaler(uint32_t spi_freq_khz) +{ + uint32_t system_clock_khz = SystemCoreClock / 1000; + + if (spi_freq_khz >= system_clock_khz / 2) + return SPI_BaudRatePrescaler_2; + else if (spi_freq_khz >= system_clock_khz / 4) + return SPI_BaudRatePrescaler_4; + else if (spi_freq_khz >= system_clock_khz / 8) + return SPI_BaudRatePrescaler_8; + else if (spi_freq_khz >= system_clock_khz / 16) + return SPI_BaudRatePrescaler_16; + else if (spi_freq_khz >= system_clock_khz / 32) + return SPI_BaudRatePrescaler_32; + else if (spi_freq_khz >= system_clock_khz / 64) + return SPI_BaudRatePrescaler_64; + else if (spi_freq_khz >= system_clock_khz / 128) + return SPI_BaudRatePrescaler_128; + else + return SPI_BaudRatePrescaler_256; +} + +static void spi_flash_send_address(uint32_t addr) +{ + if (spi_conf.addr_bytes >= 4) + spi_flash_send_byte(ADDR_4th_CYCLE(addr)); + + spi_flash_send_byte(ADDR_3rd_CYCLE(addr)); + spi_flash_send_byte(ADDR_2nd_CYCLE(addr)); + spi_flash_send_byte(ADDR_1st_CYCLE(addr)); +} + +static int spi_flash_init(void *conf, uint32_t conf_size) +{ + SPI_InitTypeDef spi_init; + + if (conf_size < sizeof(spi_conf_t)) + return -1; + spi_conf = *(spi_conf_t *)conf; + + spi_flash_gpio_init(); + + spi_flash_deselect_chip(); + + /* Configure SPI */ + spi_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + spi_init.SPI_Mode = SPI_Mode_Master; + spi_init.SPI_DataSize = SPI_DataSize_8b; + spi_init.SPI_CPOL = SPI_CPOL_High; + spi_init.SPI_CPHA = SPI_CPHA_2Edge; + spi_init.SPI_NSS = SPI_NSS_Soft; + spi_init.SPI_BaudRatePrescaler = + spi_flash_get_baud_rate_prescaler(spi_conf.freq); + spi_init.SPI_FirstBit = SPI_FirstBit_MSB; + spi_init.SPI_CRCPolynomial = 7; + SPI_Init(SPI1, &spi_init); + + /* Enable SPI */ + SPI_Cmd(SPI1, ENABLE); + + /* 进入4字节地址模式(如果需要) */ + if (spi_conf.addr_bytes == 4 && spi_conf.enter_4byte_cmd != UNDEFINED_CMD) + { + spi_flash_select_chip(); + spi_flash_send_byte(spi_conf.enter_4byte_cmd); + spi_flash_deselect_chip(); + } + + return 0; +} + +static void spi_flash_uninit() +{ + /* 退出4字节地址模式(如果需要) */ + if (spi_conf.addr_bytes == 4 && spi_conf.exit_4byte_cmd != UNDEFINED_CMD) + { + spi_flash_select_chip(); + spi_flash_send_byte(spi_conf.exit_4byte_cmd); + spi_flash_deselect_chip(); + } + + spi_flash_gpio_uninit(); + + /* Disable SPI */ + SPI_Cmd(SPI1, DISABLE); +} + +static uint8_t spi_flash_send_byte(uint8_t byte) +{ + /* Loop while DR register in not emplty */ + while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); + + /* Send byte through the SPI1 peripheral to generate clock signal */ + SPI_I2S_SendData(SPI1, byte); + + /* Wait to receive a byte */ + while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); + + /* Return the byte read from the SPI bus */ + return SPI_I2S_ReceiveData(SPI1); +} + +static inline uint8_t spi_flash_read_byte() +{ + return spi_flash_send_byte(FLASH_DUMMY_BYTE); +} + +static uint32_t spi_flash_read_status() +{ + uint8_t status; + uint32_t flash_status = FLASH_READY; + + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.status_cmd); + + status = spi_flash_read_byte(); + + if (spi_conf.busy_state == 1 && (status & (1 << spi_conf.busy_bit))) + flash_status = FLASH_BUSY; + else if (spi_conf.busy_state == 0 && !(status & (1 << spi_conf.busy_bit))) + flash_status = FLASH_BUSY; + + spi_flash_deselect_chip(); + + return flash_status; +} + +static uint32_t spi_flash_get_status() +{ + uint32_t status, timeout = 0x1000000; + + status = spi_flash_read_status(); + + /* Wait for an operation to complete or a TIMEOUT to occur */ + while (status == FLASH_BUSY && timeout) + { + status = spi_flash_read_status(); + timeout --; + } + + if (!timeout) + status = FLASH_TIMEOUT; + + return status; +} + +static void spi_flash_read_id(chip_id_t *chip_id) +{ + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.read_id_cmd); + + chip_id->maker_id = spi_flash_read_byte(); + chip_id->device_id = spi_flash_read_byte(); + chip_id->third_id = spi_flash_read_byte(); + chip_id->fourth_id = spi_flash_read_byte(); + + spi_flash_deselect_chip(); +} + +static void spi_flash_write_enable() +{ + if (spi_conf.write_en_cmd == UNDEFINED_CMD) + return; + + spi_flash_select_chip(); + spi_flash_send_byte(spi_conf.write_en_cmd); + spi_flash_deselect_chip(); +} + +static void spi_flash_write_page_async(uint8_t *buf, uint32_t page, + uint32_t page_size) +{ + uint32_t i; + + spi_flash_write_enable(); + + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.write_cmd); + + page = page << spi_conf.page_offset; + + spi_flash_send_address(page); + + for (i = 0; i < page_size; i++) + spi_flash_send_byte(buf[i]); + + spi_flash_deselect_chip(); +} + +static uint32_t spi_flash_read_data(uint8_t *buf, uint32_t page, + uint32_t page_offset, uint32_t data_size) +{ + uint32_t i, addr = (page << spi_conf.page_offset) + page_offset; + + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.read_cmd); + + spi_flash_send_address(addr); + + /* AT45DB requires write of dummy byte after address */ + spi_flash_send_byte(FLASH_DUMMY_BYTE); + + for (i = 0; i < data_size; i++) + buf[i] = spi_flash_read_byte(); + + spi_flash_deselect_chip(); + + return FLASH_READY; +} + +static uint32_t spi_flash_read_page(uint8_t *buf, uint32_t page, + uint32_t page_size) +{ + return spi_flash_read_data(buf, page, 0, page_size); +} + +static uint32_t spi_flash_read_spare_data(uint8_t *buf, uint32_t page, + uint32_t offset, uint32_t data_size) +{ + return FLASH_STATUS_INVALID_CMD; +} + +static uint32_t spi_flash_erase_block(uint32_t page) +{ + uint32_t addr = page << spi_conf.page_offset; + + spi_flash_write_enable(); + + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.erase_cmd); + + spi_flash_send_address(addr); + + spi_flash_deselect_chip(); + + return spi_flash_get_status(); +} + +static inline bool spi_flash_is_bb_supported() +{ + return false; +} + +flash_hal_t hal_spi = +{ + .init = spi_flash_init, + .uninit = spi_flash_uninit, + .read_id = spi_flash_read_id, + .erase_block = spi_flash_erase_block, + .read_page = spi_flash_read_page, + .read_spare_data = spi_flash_read_spare_data, + .write_page_async = spi_flash_write_page_async, + .read_status = spi_flash_read_status, + .is_bb_supported = spi_flash_is_bb_supported +}; diff --git a/firmware/programmer/spi_nor_flash.c.bak b/firmware/programmer/spi_nor_flash.c.bak new file mode 100644 index 0000000..d556f28 --- /dev/null +++ b/firmware/programmer/spi_nor_flash.c.bak @@ -0,0 +1,403 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nor_flash.h" +#include + +#define SPI_FLASH_CS_PIN GPIO_Pin_4 +#define SPI_FLASH_SCK_PIN GPIO_Pin_5 +#define SPI_FLASH_MISO_PIN GPIO_Pin_6 +#define SPI_FLASH_MOSI_PIN GPIO_Pin_7 + +#define FLASH_DUMMY_BYTE 0xA5 + +/* 1st addressing cycle */ +#define ADDR_1st_CYCLE(ADDR) (uint8_t)((ADDR)& 0xFF) +/* 2nd addressing cycle */ +#define ADDR_2nd_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF00) >> 8) +/* 3rd addressing cycle */ +#define ADDR_3rd_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF0000) >> 16) +/* 4th addressing cycle */ +#define ADDR_4th_CYCLE(ADDR) (uint8_t)(((ADDR)& 0xFF000000) >> 24) + +#define UNDEFINED_CMD 0xFF + +typedef struct __attribute__((__packed__)) +{ + uint8_t page_offset; + uint8_t read_cmd; + uint8_t read_id_cmd; + uint8_t write_cmd; + uint8_t write_en_cmd; + uint8_t erase_cmd; + uint8_t status_cmd; + uint8_t busy_bit; + uint8_t busy_state; + uint8_t addr_bytes; // ַֽ34 + uint8_t enter_4byte_cmd; // 4ֽģʽ + uint8_t exit_4byte_cmd; // ˳4ֽģʽ + uint32_t freq; +} spi_conf_t; + +static spi_conf_t spi_conf; + +static void spi_flash_gpio_init() +{ + GPIO_InitTypeDef gpio_init; + + RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); + + /* Enable SPI peripheral clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); + + /* Configure SPI SCK pin */ + gpio_init.GPIO_Pin = SPI_FLASH_SCK_PIN; + gpio_init.GPIO_Speed = GPIO_Speed_50MHz; + gpio_init.GPIO_Mode = GPIO_Mode_AF_PP; + GPIO_Init(GPIOA, &gpio_init); + + /* Configure SPI MOSI pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MOSI_PIN; + GPIO_Init(GPIOA, &gpio_init); + + /* Configure SPI MISO pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MISO_PIN; + gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOA, &gpio_init); + + /* Configure SPI CS pin */ + gpio_init.GPIO_Pin = SPI_FLASH_CS_PIN; + gpio_init.GPIO_Mode = GPIO_Mode_Out_PP; + GPIO_Init(GPIOA, &gpio_init); +} + +static void spi_flash_gpio_uninit() +{ + GPIO_InitTypeDef gpio_init; + + /* Disable SPI peripheral clock */ + RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, DISABLE); + + /* Disable SPI SCK pin */ + gpio_init.GPIO_Pin = SPI_FLASH_SCK_PIN; + gpio_init.GPIO_Mode = GPIO_Mode_IN_FLOATING; + GPIO_Init(GPIOA, &gpio_init); + + /* Disable SPI MISO pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MISO_PIN; + GPIO_Init(GPIOA, &gpio_init); + + /* Disable SPI MOSI pin */ + gpio_init.GPIO_Pin = SPI_FLASH_MOSI_PIN; + GPIO_Init(GPIOA, &gpio_init); + + /* Disable SPI CS pin */ + gpio_init.GPIO_Pin = SPI_FLASH_CS_PIN; + GPIO_Init(GPIOA, &gpio_init); +} + +static inline void spi_flash_select_chip() +{ + GPIO_ResetBits(GPIOA, SPI_FLASH_CS_PIN); +} + +static inline void spi_flash_deselect_chip() +{ + GPIO_SetBits(GPIOA, SPI_FLASH_CS_PIN); +} + +static uint16_t spi_flash_get_baud_rate_prescaler(uint32_t spi_freq_khz) +{ + uint32_t system_clock_khz = SystemCoreClock / 1000; + + if (spi_freq_khz >= system_clock_khz / 2) + return SPI_BaudRatePrescaler_2; + else if (spi_freq_khz >= system_clock_khz / 4) + return SPI_BaudRatePrescaler_4; + else if (spi_freq_khz >= system_clock_khz / 8) + return SPI_BaudRatePrescaler_8; + else if (spi_freq_khz >= system_clock_khz / 16) + return SPI_BaudRatePrescaler_16; + else if (spi_freq_khz >= system_clock_khz / 32) + return SPI_BaudRatePrescaler_32; + else if (spi_freq_khz >= system_clock_khz / 64) + return SPI_BaudRatePrescaler_64; + else if (spi_freq_khz >= system_clock_khz / 128) + return SPI_BaudRatePrescaler_128; + else + return SPI_BaudRatePrescaler_256; +} + +static uint8_t spi_flash_send_byte(uint8_t byte) +{ + /* Loop while DR register in not emplty */ + while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); + + /* Send byte through the SPI1 peripheral to generate clock signal */ + SPI_I2S_SendData(SPI1, byte); + + /* Wait to receive a byte */ + while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET); + + /* Return the byte read from the SPI bus */ + return SPI_I2S_ReceiveData(SPI1); +} + +static inline uint8_t spi_flash_read_byte() +{ + return spi_flash_send_byte(FLASH_DUMMY_BYTE); +} + +// ͵ַֽڣ֧3ֽں4ֽģʽ +static void spi_flash_send_address(uint32_t addr) +{ + if (spi_conf.addr_bytes == 4) + { + // 4ֽڵַģʽ + spi_flash_send_byte(ADDR_4th_CYCLE(addr)); + spi_flash_send_byte(ADDR_3rd_CYCLE(addr)); + spi_flash_send_byte(ADDR_2nd_CYCLE(addr)); + spi_flash_send_byte(ADDR_1st_CYCLE(addr)); + } + else + { + // Ĭ3ֽڵַģʽ + spi_flash_send_byte(ADDR_3rd_CYCLE(addr)); + spi_flash_send_byte(ADDR_2nd_CYCLE(addr)); + spi_flash_send_byte(ADDR_1st_CYCLE(addr)); + } +} + +// 4ֽڵַģʽ +static void spi_flash_enter_4byte_mode(void) +{ + if (spi_conf.enter_4byte_cmd != UNDEFINED_CMD) + { + spi_flash_select_chip(); + spi_flash_send_byte(spi_conf.enter_4byte_cmd); + spi_flash_deselect_chip(); + } +} + +// ˳4ֽڵַģʽ +static void spi_flash_exit_4byte_mode(void) +{ + if (spi_conf.exit_4byte_cmd != UNDEFINED_CMD) + { + spi_flash_select_chip(); + spi_flash_send_byte(spi_conf.exit_4byte_cmd); + spi_flash_deselect_chip(); + } +} + +static int spi_flash_init(void *conf, uint32_t conf_size) +{ + SPI_InitTypeDef spi_init; + + if (conf_size < sizeof(spi_conf_t)) + return -1; + spi_conf = *(spi_conf_t *)conf; + + spi_flash_gpio_init(); + + spi_flash_deselect_chip(); + + /* Configure SPI */ + spi_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex; + spi_init.SPI_Mode = SPI_Mode_Master; + spi_init.SPI_DataSize = SPI_DataSize_8b; + spi_init.SPI_CPOL = SPI_CPOL_High; + spi_init.SPI_CPHA = SPI_CPHA_2Edge; + spi_init.SPI_NSS = SPI_NSS_Soft; + spi_init.SPI_BaudRatePrescaler = + spi_flash_get_baud_rate_prescaler(spi_conf.freq); + spi_init.SPI_FirstBit = SPI_FirstBit_MSB; + spi_init.SPI_CRCPolynomial = 7; + SPI_Init(SPI1, &spi_init); + + /* Enable SPI */ + SPI_Cmd(SPI1, ENABLE); + + // Ϊ4ֽڵַģʽͽ4ֽģʽ + if (spi_conf.addr_bytes == 4) + { + spi_flash_enter_4byte_mode(); + } + + return 0; +} + +static void spi_flash_uninit() +{ + // ǰ4ֽڵַģʽ˳4ֽģʽ + if (spi_conf.addr_bytes == 4) + { + spi_flash_exit_4byte_mode(); + } + + spi_flash_gpio_uninit(); + + /* Disable SPI */ + SPI_Cmd(SPI1, DISABLE); // ӦSPI1SPI3 +} + +static uint32_t spi_flash_read_status() +{ + uint8_t status; + uint32_t flash_status = FLASH_STATUS_READY; + + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.status_cmd); + + status = spi_flash_read_byte(); + + if (spi_conf.busy_state == 1 && (status & (1 << spi_conf.busy_bit))) + flash_status = FLASH_STATUS_BUSY; + else if (spi_conf.busy_state == 0 && !(status & (1 << spi_conf.busy_bit))) + flash_status = FLASH_STATUS_BUSY; + + spi_flash_deselect_chip(); + + return flash_status; +} + +static uint32_t spi_flash_get_status() +{ + uint32_t status, timeout = 0x1000000; + + status = spi_flash_read_status(); + + /* Wait for an operation to complete or a TIMEOUT to occur */ + while (status == FLASH_STATUS_BUSY && timeout) + { + status = spi_flash_read_status(); + timeout --; + } + + if (!timeout) + status = FLASH_STATUS_TIMEOUT; + + return status; +} + +static void spi_flash_read_id(chip_id_t *chip_id) +{ + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.read_id_cmd); + + chip_id->maker_id = spi_flash_read_byte(); + chip_id->device_id = spi_flash_read_byte(); + chip_id->third_id = spi_flash_read_byte(); + chip_id->fourth_id = spi_flash_read_byte(); + chip_id->fifth_id = spi_flash_read_byte(); + chip_id->sixth_id = spi_flash_read_byte(); + + spi_flash_deselect_chip(); +} + +static void spi_flash_write_enable() +{ + if (spi_conf.write_en_cmd == UNDEFINED_CMD) + return; + + spi_flash_select_chip(); + spi_flash_send_byte(spi_conf.write_en_cmd); + spi_flash_deselect_chip(); +} + +static void spi_flash_write_page_async(uint8_t *buf, uint32_t page, + uint32_t page_size) +{ + uint32_t i; + + spi_flash_write_enable(); + + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.write_cmd); + + page = page << spi_conf.page_offset; + + // ʹͳһĵַͺ + spi_flash_send_address(page); + + for (i = 0; i < page_size; i++) + spi_flash_send_byte(buf[i]); + + spi_flash_deselect_chip(); +} + +static uint32_t spi_flash_read_data(uint8_t *buf, uint32_t page, + uint32_t page_offset, uint32_t data_size) +{ + uint32_t i, addr = (page << spi_conf.page_offset) + page_offset; + + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.read_cmd); + + // ʹͳһĵַͺ + spi_flash_send_address(addr); + + /* AT45DB requires write of dummy byte after address */ + spi_flash_send_byte(FLASH_DUMMY_BYTE); + + for (i = 0; i < data_size; i++) + buf[i] = spi_flash_read_byte(); + + spi_flash_deselect_chip(); + + return FLASH_STATUS_READY; +} + +static uint32_t spi_flash_read_page(uint8_t *buf, uint32_t page, + uint32_t page_size) +{ + return spi_flash_read_data(buf, page, 0, page_size); +} + +static uint32_t spi_flash_read_spare_data(uint8_t *buf, uint32_t page, + uint32_t offset, uint32_t data_size) +{ + return FLASH_STATUS_INVALID_CMD; +} + +static uint32_t spi_flash_erase_block(uint32_t page) +{ + uint32_t addr = page << spi_conf.page_offset; + + spi_flash_write_enable(); + + spi_flash_select_chip(); + + spi_flash_send_byte(spi_conf.erase_cmd); + + // ʹͳһĵַͺ + spi_flash_send_address(addr); + + spi_flash_deselect_chip(); + + return spi_flash_get_status(); +} + +static inline bool spi_flash_is_bb_supported() +{ + return false; +} + +flash_hal_t hal_spi_nor = +{ + .init = spi_flash_init, + .uninit = spi_flash_uninit, + .read_id = spi_flash_read_id, + .erase_block = spi_flash_erase_block, + .read_page = spi_flash_read_page, + .read_spare_data = spi_flash_read_spare_data, + .write_page_async = spi_flash_write_page_async, + .read_status = spi_flash_read_status, + .is_bb_supported = spi_flash_is_bb_supported +}; \ No newline at end of file diff --git a/firmware/programmer/spi_nor_flash.h b/firmware/programmer/spi_nor_flash.h new file mode 100644 index 0000000..12abe07 --- /dev/null +++ b/firmware/programmer/spi_nor_flash.h @@ -0,0 +1,13 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef _SPI_NOR_FLASH_H_ +#define _SPI_NOR_FLASH_H_ + +#include "flash_hal.h" + +extern flash_hal_t hal_spi_nor; + +#endif /* _SPI_NOR_FLASH_H_ */ diff --git a/firmware/programmer/syscalls.c b/firmware/programmer/syscalls.c index 2b2a960..420221c 100644 --- a/firmware/programmer/syscalls.c +++ b/firmware/programmer/syscalls.c @@ -61,3 +61,8 @@ int _read(int file, char *ptr, int len) { return 0; } + +void abort(void) +{ + while(1); +} diff --git a/firmware/programmer/version.h b/firmware/programmer/version.h index 451a906..6b66d99 100644 --- a/firmware/programmer/version.h +++ b/firmware/programmer/version.h @@ -8,6 +8,6 @@ #define SW_VERSION_MAJOR 3 #define SW_VERSION_MINOR 5 -#define SW_VERSION_BUILD 0 +#define SW_VERSION_BUILD 1 #endif diff --git a/qt/main_window.cpp b/qt/main_window.cpp index 045c1b7..296469b 100644 --- a/qt/main_window.cpp +++ b/qt/main_window.cpp @@ -66,6 +66,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui->lastSpinBox->setEnabled(false); prog = new Programmer(this); + connect(prog, SIGNAL(connectCompleted(quint64)), this, + SLOT(slotProgConnectCompleted(quint64))); updateProgSettings(); updateChipList(); @@ -87,6 +89,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), SLOT(slotProgWrite())); connect(ui->actionReadBadBlocks, SIGNAL(triggered()), this, SLOT(slotProgReadBadBlocks())); + connect(ui->actionCheckEmpty, SIGNAL(triggered()), this, + SLOT(slotProgCheckEmpty())); connect(ui->actionProgrammer, SIGNAL(triggered()), this, SLOT(slotSettingsProgrammer())); connect(ui->actionParallelChipDb, SIGNAL(triggered()), this, @@ -109,6 +113,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui->filePathLineEdit->setText(settings.value(SETTINGS_WORK_FILE_PATH, ui->filePathLineEdit->text()).toString()); ui->dataViewer->setFile(ui->filePathLineEdit->text()); + isEmptyChip = true; } MainWindow::~MainWindow() @@ -677,6 +682,83 @@ void MainWindow::slotProgReadBadBlocks() prog->readChipBadBlocks(); } +void MainWindow::slotProgCheckEmptyCompleted(quint64 readBytes) +{ + Q_UNUSED(readBytes); + + disconnect(prog, SIGNAL(readChipProgress(quint64)), this, + SLOT(slotProgCheckEmptyProgress(quint64))); + disconnect(prog, SIGNAL(readChipCompleted(quint64)), this, + SLOT(slotProgCheckEmptyCompleted(quint64))); + + setProgress(100); + buffer.clear(); + + if (isEmptyChip) + { + qInfo() << "Check for emptiness completed: The chip is empty"; + } + else + { + qInfo() << "Check for emptiness completed: The chip is not empty"; + } +} + +void MainWindow::slotProgCheckEmptyProgress(quint64 progress) +{ + uint32_t progressPercent; + + progressPercent = progress * 100ULL / areaSize; + setProgress(progressPercent); + + for (int i = 0; i < buffer.size(); i++) + { + if (buffer[i] != 0xFF) + { + isEmptyChip = false; + + } + } + + buffer.clear(); +} + +void MainWindow::slotProgCheckEmpty() +{ + int index = ui->chipSelectComboBox->currentIndex(); + if (index <= CHIP_INDEX_DEFAULT) + { + qInfo() << "No chip selected"; + QMessageBox::warning(this, tr("Warning"), tr("Please select the chip first")); + return; + } + + isEmptyChip = true; + + quint64 start_address = + ui->blockSizeValueLabel->text().toULongLong(nullptr, 16) + * ui->firstSpinBox->value(); + areaSize = + ui->blockSizeValueLabel->text().toULongLong(nullptr, 16) + * (ui->lastSpinBox->value() + 1) - start_address; + + if (!areaSize) + { + qCritical() << "The chip size is not set"; + return; + } + + qInfo() << "Start checking for empty operations ..."; + setProgress(0); + + connect(prog, SIGNAL(readChipCompleted(quint64)), this, + SLOT(slotProgCheckEmptyCompleted(quint64))); + connect(prog, SIGNAL(readChipProgress(quint64)), this, + SLOT(slotProgCheckEmptyProgress(quint64))); + + buffer.clear(); + prog->readChip(&buffer, start_address, areaSize, true); +} void MainWindow::slotProgSelectCompleted(quint64 status) { disconnect(prog, SIGNAL(confChipCompleted(quint64)), this, diff --git a/qt/main_window.h b/qt/main_window.h index 1e7d384..fdce0f0 100644 --- a/qt/main_window.h +++ b/qt/main_window.h @@ -38,6 +38,7 @@ class MainWindow : public QMainWindow QFile workFile; quint64 areaSize; uint32_t pageSize; + bool isEmptyChip; void initBufTable(); void resetBufTable(); @@ -68,6 +69,8 @@ private slots: void slotProgDetectChipReadChipIdCompleted(quint64 status); void slotProgFirmwareUpdateCompleted(int status); void slotProgFirmwareUpdateProgress(quint64 progress); + void slotProgCheckEmptyCompleted(quint64 readBytes); + void slotProgCheckEmptyProgress(quint64 progress); void slotSelectFilePath(); void slotFilePathEditingFinished(); @@ -79,11 +82,13 @@ public slots: void slotProgVerify(); void slotProgWrite(); void slotProgReadBadBlocks(); + void slotProgCheckEmpty(); void slotSelectChip(int selectedChipNum); void slotDetectChip(); void slotSettingsProgrammer(); void slotSettingsParallelChipDb(); void slotSettingsSpiChipDb(); + void slotSettingsSpiNandDb(); void slotAboutDialog(); void slotFirmwareUpdateDialog(); }; diff --git a/qt/main_window.ui b/qt/main_window.ui index 8317596..a83351e 100644 --- a/qt/main_window.ui +++ b/qt/main_window.ui @@ -7,7 +7,7 @@ 0 0 600 - 570 + 800 @@ -123,7 +123,7 @@ - 50 + 66 16777215 @@ -158,12 +158,6 @@ 8 - - true - - - false - @@ -310,23 +304,6 @@ 21 - - - Device - - - - - - - - - - - Programmer - - - Settings @@ -343,31 +320,44 @@ - - + + false + TopToolBarArea false + toolBar + + false + TopToolBarArea false + + + + + + + + false @@ -410,7 +400,7 @@ Programmer - + false @@ -426,6 +416,14 @@ Read bad blocks + + + false + + + Blank + + Parallel chip database diff --git a/qt/nando_spi_nor_db.csv b/qt/nando_spi_nor_db.csv new file mode 100644 index 0000000..432298d --- /dev/null +++ b/qt/nando_spi_nor_db.csv @@ -0,0 +1,2 @@ +# name, page size, block size, total size, page off., read com., read ID com., write com., write en. com., erase com., status com., Busy bit, Busy bit state, Addr bytes, Enter 4-byte com., Exit 4-byte com., Max. freq. kHz, ID1, ID2, ID3, ID4, ID5 +AT45DB021D, 264, 2112, 270336, 9, 11, 159, 130, -, 80, 215, 7, 0, 3, 183, 233, 66000, 31, 35, -, -, - \ No newline at end of file diff --git a/qt/spi_nand_db.cpp b/qt/spi_nand_db.cpp new file mode 100644 index 0000000..a908845 --- /dev/null +++ b/qt/spi_nand_db.cpp @@ -0,0 +1,254 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nand_db.h" +#include +#include +#include + +SpiNandDb::SpiNandDb() +{ + readFromCvs(); +} + +SpiNandDb::~SpiNandDb() +{ +} + +ChipInfo *SpiNandDb::stringToChipInfo(const QString &s) +{ + int paramNum; + quint64 paramValue; + QStringList paramsList; + SpiNandInfo *ci = new SpiNandInfo(); + + paramsList = s.split(','); + paramNum = paramsList.size(); + if (paramNum != CHIP_PARAM_NUM) + { + QMessageBox::critical(nullptr, QObject::tr("错误"), + QObject::tr("无法正常读取芯片数据库条目. 需要 %2 个参数,数据库存在 %3个参数,请确认数据库参数").arg(CHIP_PARAM_NUM).arg(paramNum)); + delete ci; + return nullptr; + } + ci->setName(paramsList[CHIP_PARAM_NAME]); + + if (getParamFromString(paramsList[CHIP_PARAM_PAGE_SIZE], paramValue)) + { + QMessageBox::critical(nullptr, QObject::tr("错误"), + QObject::tr("无法分析参数 %1") + .arg(paramsList[CHIP_PARAM_PAGE_SIZE])); + delete ci; + return nullptr; + } + ci->setPageSize(paramValue); + + if (getParamFromString(paramsList[CHIP_PARAM_BLOCK_SIZE], paramValue)) + { + QMessageBox::critical(nullptr, QObject::tr("错误"), + QObject::tr("无法分析参数 %1") + .arg(paramsList[CHIP_PARAM_BLOCK_SIZE])); + delete ci; + return nullptr; + } + ci->setBlockSize(paramValue); + + if (getParamFromString(paramsList[CHIP_PARAM_TOTAL_SIZE], paramValue)) + { + QMessageBox::critical(nullptr, QObject::tr("错误"), + QObject::tr("无法分析参数 %1") + .arg(paramsList[CHIP_PARAM_TOTAL_SIZE])); + delete ci; + return nullptr; + } + ci->setTotalSize(paramValue); + + if (getParamFromString(paramsList[CHIP_PARAM_SPARE_SIZE], paramValue)) + { + QMessageBox::critical(nullptr, QObject::tr("错误"), + QObject::tr("无法分析参数 %1") + .arg(paramsList[CHIP_PARAM_SPARE_SIZE])); + delete ci; + return nullptr; + } + ci->setSpareSize(paramValue); + + //ci->setBBMarkOffset(0xFF); + + for (int i = CHIP_PARAM_MODE_DATA; i < CHIP_PARAM_NUM; i++) + { + if (getOptParamFromString(paramsList[i], paramValue)) + { + QMessageBox::critical(nullptr, QObject::tr("错误"), + QObject::tr("无法分析参数 %1").arg(paramsList[i])); + delete ci; + return nullptr; + } + ci->setParam(i - CHIP_PARAM_MODE_DATA, paramValue); + } + + return ci; +} + +int SpiNandDb::chipInfoToString(ChipInfo *chipInfo, QString &s) +{ + QString csvValue; + QStringList paramsList; + SpiNandInfo *ci = dynamic_cast(chipInfo); + + paramsList.append(ci->getName()); + getStringFromParam(ci->getPageSize(), csvValue); + paramsList.append(csvValue); + getStringFromParam(ci->getBlockSize(), csvValue); + paramsList.append(csvValue); + getStringFromParam(ci->getTotalSize(), csvValue); + paramsList.append(csvValue); + getStringFromParam(ci->getSpareSize(), csvValue); + paramsList.append(csvValue); + for (int i = CHIP_PARAM_MODE_DATA; i < CHIP_PARAM_NUM; i++) + { + if (getStringFromOptParam(ci->getParam(i - CHIP_PARAM_MODE_DATA), + csvValue)) + { + return -1; + } + paramsList.append(csvValue); + } + + s = paramsList.join(", "); + + return 0; +} + +QString SpiNandDb::getDbFileName() +{ + return dbFileName; +} + +ChipInfo *SpiNandDb::chipInfoGetByName(QString name) +{ + for(int i = 0; i < chipInfoVector.size(); i++) + { + if (!chipInfoVector[i]->getName().compare(name)) + return chipInfoVector[i]; + } + + return nullptr; +} + +int SpiNandDb::getIdByChipId(uint32_t id1, uint32_t id2, uint32_t id3, + uint32_t id4, uint32_t id5) +{ + for(int i = 0; i < chipInfoVector.size(); i++) + { + // Mandatory IDs + if (id1 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID1) || + id2 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID2)) + { + continue; + } + + // Optinal IDs + if (chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID3) == + ChipDb::paramNotDefValue) + { + return i; + } + if (id3 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID3)) + continue; + + if (chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID4) == + ChipDb::paramNotDefValue) + { + return i; + } + if (id4 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID4)) + continue; + + if (chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID5) == + ChipDb::paramNotDefValue) + { + return i; + } + if (id5 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID5)) + continue; + + return i; + } + + return -1; +} + +QString SpiNandDb::getNameByChipId(uint32_t id1, uint32_t id2, + uint32_t id3, uint32_t id4, uint32_t id5, uint32_t id6) +{ + for(int i = 0; i < chipInfoVector.size(); i++) + { + // Mandatory IDs + if (id1 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID1) || + id2 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID2)) + { + continue; + } + + // Optinal IDs + if (chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID3) == + ChipDb::paramNotDefValue) + { + return chipInfoVector[i]->getName(); + } + if (id3 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID3)) + continue; + + if (chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID4) == + ChipDb::paramNotDefValue) + { + return chipInfoVector[i]->getName(); + } + if (id4 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID4)) + continue; + + if (chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID5) == + ChipDb::paramNotDefValue) + { + return chipInfoVector[i]->getName(); + } + if (id5 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID5)) + continue; + + if (chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID6) == + ChipDb::paramNotDefValue) + { + return chipInfoVector[i]->getName(); + } + if (id6 != chipInfoVector[i]->getParam(SpiNandInfo::CHIP_PARAM_ID6)) + continue; + + return chipInfoVector[i]->getName(); + } + + return QString(); +} + +quint64 SpiNandDb::getChipParam(int chipIndex, int paramIndex) +{ + SpiNandInfo *ci = dynamic_cast(getChipInfo(chipIndex)); + + if (!ci || paramIndex < 0) + return 0; + + return ci->getParam(paramIndex); +} + +int SpiNandDb::setChipParam(int chipIndex, int paramIndex, + quint64 paramValue) +{ + SpiNandInfo *ci = dynamic_cast(getChipInfo(chipIndex)); + + if (!ci || paramIndex < 0) + return -1; + + return ci->setParam(paramIndex, paramValue); +} diff --git a/qt/spi_nand_db.h b/qt/spi_nand_db.h new file mode 100644 index 0000000..28d3cb1 --- /dev/null +++ b/qt/spi_nand_db.h @@ -0,0 +1,64 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef SPI_NAND_DB_H +#define SPI_NAND_DB_H + +#include "chip_db.h" +#include "spi_nand_info.h" + +#include +#include +#include +#include +#include + +class SpiNandDb : public ChipDb +{ +private: + QString dbFileName = "nando_spi_nand_db.csv"; + +protected: + QString getDbFileName() override; + ChipInfo *stringToChipInfo(const QString &s) override; + int chipInfoToString(ChipInfo *chipInfo, QString &s) override; + +public: + enum + { + CHIP_PARAM_NAME, + CHIP_PARAM_PAGE_SIZE, + CHIP_PARAM_BLOCK_SIZE, + CHIP_PARAM_TOTAL_SIZE, + CHIP_PARAM_SPARE_SIZE, + CHIP_PARAM_MODE_DATA, + CHIP_PARAM_UNLOCK_DATA, + CHIP_PARAM_ECC_ERR_BITS_MASK, + CHIP_PARAM_ECC_ERR_BITS_STATE, + CHIP_PARAM_READ_DUMMY_PREPEND, + CHIP_PARAM_PLANE_SELECT_HAVE, + CHIP_PARAM_DIE_SELECT_TYPE, + CHIP_PARAM_FREQ, + CHIP_PARAM_ID1, + CHIP_PARAM_ID2, + CHIP_PARAM_ID3, + CHIP_PARAM_ID4, + CHIP_PARAM_ID5, + CHIP_PARAM_NUM + }; + + explicit SpiNandDb(); + virtual ~SpiNandDb(); + + ChipInfo *chipInfoGetByName(QString name); + int getIdByChipId(uint32_t id1, uint32_t id2, uint32_t id3, uint32_t id4, + uint32_t id5); + QString getNameByChipId(uint32_t id1, uint32_t id2, + uint32_t id3, uint32_t id4, uint32_t id5, uint32_t id6) override; + quint64 getChipParam(int chipIndex, int paramIndex); + int setChipParam(int chipIndex, int paramIndex, quint64 paramValue); +}; + +#endif // SPI_NAND_DB_H diff --git a/qt/spi_nand_db_dialog.cpp b/qt/spi_nand_db_dialog.cpp new file mode 100644 index 0000000..275da87 --- /dev/null +++ b/qt/spi_nand_db_dialog.cpp @@ -0,0 +1,85 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nand_db_dialog.h" +#include "ui_spi_nand_db_dialog.h" + +//#define HEADER_LONG_WIDTH 120 +//#define HEADER_MED_WIDTH 110 +//#define HEADER_SHORT_WIDTH 50 + +SpiNandDbDialog::SpiNandDbDialog(SpiNandDb *chipDb, QWidget *parent) : + QDialog(parent), ui(new Ui::SpiNandDbDialog), + chipDbTableModel(chipDb, parent) +{ + ui->setupUi(this); + +#ifdef Q_OS_WIN32 + QFont font("Courier New", 10); + ui->chipDbTableView->setFont(font); +#endif + + chipDbProxyModel.setSourceModel(&chipDbTableModel); + ui->chipDbTableView->setModel(&chipDbProxyModel); +// ui->chipDbTableView->setColumnWidth(SpiNandDb::CHIP_PARAM_NAME, +// HEADER_LONG_WIDTH); +// ui->chipDbTableView->setColumnWidth(SpiNandDb::CHIP_PARAM_PAGE_SIZE, +// HEADER_MED_WIDTH); +// ui->chipDbTableView->setColumnWidth(SpiNandDb::CHIP_PARAM_BLOCK_SIZE, +// HEADER_MED_WIDTH); +// ui->chipDbTableView->setColumnWidth(SpiNandDb::CHIP_PARAM_TOTAL_SIZE, +// HEADER_MED_WIDTH); +// ui->chipDbTableView->setColumnWidth(SpiNandDb::CHIP_PARAM_SPARE_SIZE, +// HEADER_MED_WIDTH); +// for (int i = SpiNandDb::CHIP_PARAM_NAME; +// i <= SpiNandDb::CHIP_PARAM_NUM - 1; i++) +// { +// //ui->chipDbTableView->setColumnWidth(i, HEADER_MED_WIDTH); +// //ui->chipDbTableView->horizontalHeader()->setSectionResizeMode(i, QHeaderView::Stretch); +// //ui->chipDbTableView->horizontalHeader()->setMinimumSectionSize(-1); + +// } + ui->chipDbTableView->resizeColumnsToContents(); + + connect(ui->addChipDbButton, SIGNAL(clicked()), this, + SLOT(slotAddChipDbButtonClicked())); + connect(ui->delChipDbButton, SIGNAL(clicked()), this, + SLOT(slotDelChipDbButtonClicked())); + connect(ui->okCancelButtonBox->button(QDialogButtonBox::Ok), + SIGNAL(clicked()), this, SLOT(slotOkButtonClicked())); + connect(ui->okCancelButtonBox->button(QDialogButtonBox::Cancel), + SIGNAL(clicked()), this, SLOT(slotCancelButtonClicked())); +} + +SpiNandDbDialog::~SpiNandDbDialog() +{ + delete ui; +} + +void SpiNandDbDialog::slotAddChipDbButtonClicked() +{ + chipDbTableModel.addRow(); +} + +void SpiNandDbDialog::slotDelChipDbButtonClicked() +{ + QModelIndexList selection = ui->chipDbTableView->selectionModel()-> + selectedRows(); + + if (!selection.count()) + return; + + chipDbTableModel.delRow(selection.at(0).row()); +} + +void SpiNandDbDialog::slotOkButtonClicked() +{ + chipDbTableModel.commit(); +} + +void SpiNandDbDialog::slotCancelButtonClicked() +{ + chipDbTableModel.reset(); +} diff --git a/qt/spi_nand_db_dialog.h b/qt/spi_nand_db_dialog.h new file mode 100644 index 0000000..057d6aa --- /dev/null +++ b/qt/spi_nand_db_dialog.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef SPI_NAND_DB_DALOG_H +#define SPI_NAND_DB_DALOG_H + +#include "spi_nand_db_table_model.h" +#include +#include + +namespace Ui { +class SpiNandDbDialog; +} + +class SpiNandDbDialog : public QDialog +{ + Q_OBJECT + + Ui::SpiNandDbDialog *ui; + SpiNandDbTableModel chipDbTableModel; + QSortFilterProxyModel chipDbProxyModel; + +public: + explicit SpiNandDbDialog(SpiNandDb *chipDb, QWidget *parent = nullptr); + ~SpiNandDbDialog(); + +private slots: + void slotAddChipDbButtonClicked(); + void slotDelChipDbButtonClicked(); + void slotOkButtonClicked(); + void slotCancelButtonClicked(); +}; + +#endif // SPI_NAND_DB_DALOG_H diff --git a/qt/spi_nand_db_dialog.ui b/qt/spi_nand_db_dialog.ui new file mode 100644 index 0000000..dc524ae --- /dev/null +++ b/qt/spi_nand_db_dialog.ui @@ -0,0 +1,208 @@ + + + SpiNandDbDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 1470 + 600 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + SPI Nand芯片数据库 + + + + + + QLayout::SetMaximumSize + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + 0 + 0 + + + + + 300 + 300 + + + + true + + + QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideNone + + + QAbstractItemView::ScrollPerItem + + + QAbstractItemView::ScrollPerItem + + + true + + + true + + + 30 + + + 70 + + + 20 + + + 30 + + + + + + + + + + 0 + 0 + + + + + 40 + 40 + + + + + 40 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + 40 + 40 + + + + + 40 + 40 + + + + - + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + okCancelButtonBox + accepted() + SpiNandDbDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + okCancelButtonBox + rejected() + SpiNandDbDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/qt/spi_nand_db_table_model.cpp b/qt/spi_nand_db_table_model.cpp new file mode 100644 index 0000000..2b79725 --- /dev/null +++ b/qt/spi_nand_db_table_model.cpp @@ -0,0 +1,361 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nand_db_table_model.h" +#include + +#define CHIP_DB_TABLE_MODEL_MIN_CYCLES 1 +#define CHIP_DB_TABLE_MODEL_MAX_CYCLES 4 + +SpiNandDbTableModel::SpiNandDbTableModel(SpiNandDb *chipDb, + QObject *parent) : QAbstractTableModel(parent) +{ + this->chipDb = chipDb; +} + +int SpiNandDbTableModel::rowCount(const QModelIndex & /*parent*/) const +{ + return chipDb->size(); +} + +int SpiNandDbTableModel::columnCount(const QModelIndex & /*parent*/) const +{ + return SpiNandDb::CHIP_PARAM_NUM; +} + +QVariant SpiNandDbTableModel::data(const QModelIndex &index, int role) const +{ + int column; + QString paramStr; + + if (role != Qt::DisplayRole && role != Qt::EditRole) + return QVariant(); + + column = index.column(); + switch (column) + { + case SpiNandDb::CHIP_PARAM_NAME: + return chipDb->getChipName(index.row()); + case SpiNandDb::CHIP_PARAM_PAGE_SIZE: + chipDb->getHexStringFromParam(chipDb->getPageSize(index.row()), + paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_BLOCK_SIZE: + chipDb->getHexStringFromParam(chipDb->getBlockSize(index.row()), + paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_TOTAL_SIZE: + chipDb->getHexStringFromParam(chipDb->getTotalSize(index.row()), + paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_SPARE_SIZE: + chipDb->getHexStringFromParam(chipDb->getSpareSize(index.row()), + paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_MODE_DATA: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_MODE_DATA), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_UNLOCK_DATA: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_UNLOCK_DATA), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_ECC_ERR_BITS_MASK: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_ECC_ERR_BITS_MASK), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_ECC_ERR_BITS_STATE: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_ECC_ERR_BITS_STATE), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_READ_DUMMY_PREPEND: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_READ_DUMMY_PREPEND), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_PLANE_SELECT_HAVE: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_PLANE_SELECT_HAVE), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_DIE_SELECT_TYPE: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_DIE_SELECT_TYPE), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_FREQ: + return (uint)chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_FREQ); + case SpiNandDb::CHIP_PARAM_ID1: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_ID1), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_ID2: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_ID2), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_ID3: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_ID3), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_ID4: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_ID4), paramStr); + return paramStr; + case SpiNandDb::CHIP_PARAM_ID5: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNandInfo::CHIP_PARAM_ID5), paramStr); + return paramStr; + } + + return QVariant(); +} + +QVariant SpiNandDbTableModel::headerData(int section, + Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + { + switch (section) + { + case SpiNandDb::CHIP_PARAM_NAME: return tr("Chip name"); + case SpiNandDb::CHIP_PARAM_PAGE_SIZE: return tr("Page\nsize"); + case SpiNandDb::CHIP_PARAM_BLOCK_SIZE: return tr("Block\nsize"); + case SpiNandDb::CHIP_PARAM_TOTAL_SIZE: return tr("Total\nsize"); + case SpiNandDb::CHIP_PARAM_SPARE_SIZE: return tr("Spare\nsize"); + case SpiNandDb::CHIP_PARAM_MODE_DATA: return tr("Mode\ndata"); + case SpiNandDb::CHIP_PARAM_UNLOCK_DATA: return tr("Unlock\ndata"); + case SpiNandDb::CHIP_PARAM_ECC_ERR_BITS_MASK: return tr("ECC err\nmask"); + case SpiNandDb::CHIP_PARAM_ECC_ERR_BITS_STATE: return tr("ECC err\nstate"); + case SpiNandDb::CHIP_PARAM_READ_DUMMY_PREPEND: return tr("Read\nprepend"); + case SpiNandDb::CHIP_PARAM_PLANE_SELECT_HAVE: return tr("Plane\nhave"); + case SpiNandDb::CHIP_PARAM_DIE_SELECT_TYPE: return tr("Die select\ntype"); + case SpiNandDb::CHIP_PARAM_FREQ: return tr("Frequency\n(kHz)"); + case SpiNandDb::CHIP_PARAM_ID1: return tr("ID 1"); + case SpiNandDb::CHIP_PARAM_ID2: return tr("ID 2"); + case SpiNandDb::CHIP_PARAM_ID3: return tr("ID 3"); + case SpiNandDb::CHIP_PARAM_ID4: return tr("ID 4"); + case SpiNandDb::CHIP_PARAM_ID5: return tr("ID 5"); + } + } + + if (role == Qt::ToolTipRole) + { + switch (section) + { + case SpiNandDb::CHIP_PARAM_NAME: + return tr("Chip name"); + case SpiNandDb::CHIP_PARAM_PAGE_SIZE: + return tr("Page size in bytes"); + case SpiNandDb::CHIP_PARAM_BLOCK_SIZE: + return tr("Block size in bytes"); + case SpiNandDb::CHIP_PARAM_TOTAL_SIZE: + return tr("Total size in bytes"); + case SpiNandDb::CHIP_PARAM_SPARE_SIZE: + return tr("User spare part size in bytes"); + case SpiNandDb::CHIP_PARAM_MODE_DATA: + return tr("Data byte for mode registr"); + case SpiNandDb::CHIP_PARAM_UNLOCK_DATA: + return tr("Data byte for unlock registr"); + case SpiNandDb::CHIP_PARAM_ECC_ERR_BITS_MASK: + return tr("ECC error bits mask."); + case SpiNandDb::CHIP_PARAM_ECC_ERR_BITS_STATE: + return tr("ECC error bits state."); + case SpiNandDb::CHIP_PARAM_READ_DUMMY_PREPEND: + return tr("Dummy mode prepend flag for read."); + case SpiNandDb::CHIP_PARAM_PLANE_SELECT_HAVE: + return tr("True if plane select have."); + case SpiNandDb::CHIP_PARAM_DIE_SELECT_TYPE: + return tr("0 or die select type number. (0/1/2)"); + case SpiNandDb::CHIP_PARAM_FREQ: + return tr("Maximum supported SPI frequency in kHz"); + case SpiNandDb::CHIP_PARAM_ID1: + return tr("Chip ID 1st byte"); + case SpiNandDb::CHIP_PARAM_ID2: + return tr("Chip ID 2nd byte"); + case SpiNandDb::CHIP_PARAM_ID3: + return tr("Chip ID 3rd byte"); + case SpiNandDb::CHIP_PARAM_ID4: + return tr("Chip ID 4th byte"); + case SpiNandDb::CHIP_PARAM_ID5: + return tr("Chip ID 5th byte"); + } + } + + return QVariant(); +} + +Qt::ItemFlags SpiNandDbTableModel::flags (const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +bool SpiNandDbTableModel::setData(const QModelIndex &index, + const QVariant &value, int role) +{ + quint64 paramVal; + + if (role != Qt::EditRole) + return false; + + switch (index.column()) + { + case SpiNandDb::CHIP_PARAM_NAME: + chipDb->setChipName(index.row(), value.toString()); + return true; + case SpiNandDb::CHIP_PARAM_PAGE_SIZE: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + chipDb->setPageSize(index.row(), paramVal); + return true; + case SpiNandDb::CHIP_PARAM_BLOCK_SIZE: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + chipDb->setBlockSize(index.row(), paramVal); + return true; + case SpiNandDb::CHIP_PARAM_TOTAL_SIZE: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + chipDb->setTotalSize(index.row(), paramVal); + return true; + case SpiNandDb::CHIP_PARAM_SPARE_SIZE: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + chipDb->setSpareSize(index.row(), paramVal); + return true; + case SpiNandDb::CHIP_PARAM_MODE_DATA: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_MODE_DATA, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_UNLOCK_DATA: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_UNLOCK_DATA, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_ECC_ERR_BITS_MASK: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_ECC_ERR_BITS_MASK, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_ECC_ERR_BITS_STATE: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_ECC_ERR_BITS_STATE, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_READ_DUMMY_PREPEND: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_READ_DUMMY_PREPEND, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_PLANE_SELECT_HAVE: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_PLANE_SELECT_HAVE, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_DIE_SELECT_TYPE: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_DIE_SELECT_TYPE, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_FREQ: + if (chipDb->getParamFromString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFFFFFFFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_FREQ, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_ID1: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_ID1, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_ID2: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_ID2, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_ID3: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_ID3, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_ID4: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_ID4, + paramVal); + return true; + case SpiNandDb::CHIP_PARAM_ID5: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNandInfo::CHIP_PARAM_ID5, + paramVal); + return true; + } + + return false; +} + +void SpiNandDbTableModel::addRow() +{ + SpiNandInfo *chipInfo = new SpiNandInfo(); + + beginResetModel(); + chipDb->addChip(chipInfo); + endResetModel(); +} + +void SpiNandDbTableModel::delRow(int index) +{ + beginResetModel(); + chipDb->delChip(index); + endResetModel(); +} + +void SpiNandDbTableModel::commit() +{ + chipDb->commit(); +} + +void SpiNandDbTableModel::reset() +{ + beginResetModel(); + chipDb->reset(); + endResetModel(); +} + diff --git a/qt/spi_nand_db_table_model.h b/qt/spi_nand_db_table_model.h new file mode 100644 index 0000000..867cdd6 --- /dev/null +++ b/qt/spi_nand_db_table_model.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef SPI_NAND_DB_TABLE_MODEL_H +#define SPI_NAND_DB_TABLE_MODEL_H + +#include "spi_nand_db.h" +#include + +class SpiNandDbTableModel : public QAbstractTableModel +{ + Q_OBJECT + + SpiNandDb *chipDb; + +public: + explicit SpiNandDbTableModel(SpiNandDb *chipDb, QObject *parent = nullptr); + int rowCount(const QModelIndex & /*parent*/) const override; + int columnCount(const QModelIndex & /*parent*/) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) + const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole) override; + void addRow(); + void delRow(int index); + void commit(); + void reset(); +}; + +#endif // SPI_NAND_DB_TABLE_MODEL_H diff --git a/qt/spi_nand_info.cpp b/qt/spi_nand_info.cpp new file mode 100644 index 0000000..2f6f45d --- /dev/null +++ b/qt/spi_nand_info.cpp @@ -0,0 +1,65 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nand_info.h" + +typedef struct __attribute__((__packed__)) +{ + uint32_t spare_offset; + uint8_t mode_data; + uint8_t unlock_data; + uint8_t ecc_err_bits_mask; + uint8_t ecc_err_bits_state; + uint8_t read_dummy_prepend; + uint8_t plane_select_have; + uint8_t die_select_type; + uint32_t freq; +} SpiNandConf; + +SpiNandInfo::SpiNandInfo() +{ + hal = CHIP_HAL_SPI_NAND; +} + +SpiNandInfo::~SpiNandInfo() +{ +} + +const QByteArray &SpiNandInfo::getHalConf() +{ + SpiNandConf conf; + conf.spare_offset = getPageSize(); + conf.mode_data = static_cast(params[CHIP_PARAM_MODE_DATA]); + conf.unlock_data = static_cast(params[CHIP_PARAM_UNLOCK_DATA]); + conf.ecc_err_bits_mask = static_cast(params[CHIP_PARAM_ECC_ERR_BITS_MASK]); + conf.ecc_err_bits_state = static_cast(params[CHIP_PARAM_ECC_ERR_BITS_STATE]); + conf.read_dummy_prepend = static_cast(params[CHIP_PARAM_READ_DUMMY_PREPEND]); + conf.plane_select_have = static_cast(params[CHIP_PARAM_PLANE_SELECT_HAVE]); + conf.die_select_type = static_cast(params[CHIP_PARAM_DIE_SELECT_TYPE]); + conf.freq = params[CHIP_PARAM_FREQ]; + + halConf.clear(); + halConf.append(reinterpret_cast(&conf), sizeof(conf)); + + return halConf; +} + +quint64 SpiNandInfo::getParam(uint32_t num) +{ + if (num >= CHIP_PARAM_NUM) + return 0; + + return params[num]; +} + +int SpiNandInfo::setParam(uint32_t num, quint64 value) +{ + if (num >= CHIP_PARAM_NUM) + return -1; + + params[num] = value; + + return 0; +} diff --git a/qt/spi_nand_info.h b/qt/spi_nand_info.h new file mode 100644 index 0000000..171ebbf --- /dev/null +++ b/qt/spi_nand_info.h @@ -0,0 +1,45 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef SPI_NAND_INFO_H +#define SPI_NAND_INFO_H + +#include "chip_info.h" + +class SpiNandInfo : public ChipInfo +{ +public: + enum + { + CHIP_PARAM_MODE_DATA, + CHIP_PARAM_UNLOCK_DATA, + CHIP_PARAM_ECC_ERR_BITS_MASK, + CHIP_PARAM_ECC_ERR_BITS_STATE, + CHIP_PARAM_READ_DUMMY_PREPEND, + CHIP_PARAM_PLANE_SELECT_HAVE, + CHIP_PARAM_DIE_SELECT_TYPE, + CHIP_PARAM_FREQ, + CHIP_PARAM_ID1, + CHIP_PARAM_ID2, + CHIP_PARAM_ID3, + CHIP_PARAM_ID4, + CHIP_PARAM_ID5, + CHIP_PARAM_ID6, + CHIP_PARAM_NUM, + }; + +private: + QByteArray halConf; + quint64 params[CHIP_PARAM_NUM] = {}; + +public: + SpiNandInfo(); + virtual ~SpiNandInfo(); + const QByteArray &getHalConf() override; + quint64 getParam(uint32_t num) override; + int setParam(uint32_t num, quint64 value) override; +}; + +#endif // SPI_NAND_INFO_H diff --git a/qt/spi_nor_db.cpp b/qt/spi_nor_db.cpp new file mode 100644 index 0000000..49caf41 --- /dev/null +++ b/qt/spi_nor_db.cpp @@ -0,0 +1,242 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nor_db.h" +#include +#include +#include + +SpiNorDb::SpiNorDb() +{ + readFromCvs(); +} + +SpiNorDb::~SpiNorDb() +{ +} + +ChipInfo *SpiNorDb::stringToChipInfo(const QString &s) +{ + int paramNum; + quint64 paramValue; + QStringList paramsList; + SpiNorInfo *ci = new SpiNorInfo(); + + paramsList = s.split(','); + paramNum = paramsList.size(); + if (paramNum != CHIP_PARAM_NUM) + { + QMessageBox::critical(nullptr, QObject::tr("Error"), + QObject::tr("Failed to read chip DB entry. Expected %2 parameters, " + "but read %3").arg(CHIP_PARAM_NUM).arg(paramNum)); + delete ci; + return nullptr; + } + + ci->setName(paramsList[CHIP_PARAM_NAME]); + if (getParamFromString(paramsList[CHIP_PARAM_PAGE_SIZE], paramValue)) + { + QMessageBox::critical(nullptr, QObject::tr("Error"), + QObject::tr("Failed to parse parameter %1") + .arg(paramsList[CHIP_PARAM_PAGE_SIZE])); + delete ci; + return nullptr; + } + ci->setPageSize(paramValue); + + if (getParamFromString(paramsList[CHIP_PARAM_BLOCK_SIZE], paramValue)) + { + QMessageBox::critical(nullptr, QObject::tr("Error"), + QObject::tr("Failed to parse parameter %1") + .arg(paramsList[CHIP_PARAM_BLOCK_SIZE])); + delete ci; + return nullptr; + } + ci->setBlockSize(paramValue); + + if (getParamFromString(paramsList[CHIP_PARAM_TOTAL_SIZE], paramValue)) + { + QMessageBox::critical(nullptr, QObject::tr("Error"), + QObject::tr("Failed to parse parameter %1") + .arg(paramsList[CHIP_PARAM_TOTAL_SIZE])); + delete ci; + return nullptr; + } + ci->setTotalSize(paramValue); + + for (int i = CHIP_PARAM_PAGE_OFF; i < CHIP_PARAM_NUM; i++) + { + if (getOptParamFromString(paramsList[i], paramValue)) + { + QMessageBox::critical(nullptr, QObject::tr("Error"), + QObject::tr("Failed to parse parameter %1").arg(paramsList[i])); + delete ci; + return nullptr; + } + ci->setParam(i - CHIP_PARAM_PAGE_OFF, paramValue); + } + + return ci; +} + +int SpiNorDb::chipInfoToString(ChipInfo *chipInfo, QString &s) +{ + QString csvValue; + QStringList paramsList; + SpiNorInfo *ci = dynamic_cast(chipInfo); + + paramsList.append(ci->getName()); + getStringFromParam(ci->getPageSize(), csvValue); + paramsList.append(csvValue); + getStringFromParam(ci->getBlockSize(), csvValue); + paramsList.append(csvValue); + getStringFromParam(ci->getTotalSize(), csvValue); + paramsList.append(csvValue); + + for (int i = CHIP_PARAM_PAGE_OFF; i < CHIP_PARAM_NUM; i++) + { + if (getStringFromOptParam(ci->getParam(i - CHIP_PARAM_PAGE_OFF), + csvValue)) + { + return -1; + } + paramsList.append(csvValue); + } + + s = paramsList.join(", "); + + return 0; +} + +QString SpiNorDb::getDbFileName() +{ + return dbFileName; +} + +ChipInfo *SpiNorDb::chipInfoGetByName(QString name) +{ + for(int i = 0; i < chipInfoVector.size(); i++) + { + if (!chipInfoVector[i]->getName().compare(name)) + return chipInfoVector[i]; + } + + return nullptr; +} + +int SpiNorDb::getIdByChipId(uint32_t id1, uint32_t id2, uint32_t id3, + uint32_t id4, uint32_t id5) +{ + for(int i = 0; i < chipInfoVector.size(); i++) + { + // Mandatory IDs + if (id1 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID1) || + id2 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID2)) + { + continue; + } + + // Optinal IDs + if (chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID3) == + ChipDb::paramNotDefValue) + { + return i; + } + if (id3 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID3)) + continue; + + if (chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID4) == + ChipDb::paramNotDefValue) + { + return i; + } + if (id4 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID4)) + continue; + + if (chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID5) == + ChipDb::paramNotDefValue) + { + return i; + } + if (id5 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID5)) + continue; + + return i; + } + + return -1; +} + +QString SpiNorDb::getNameByChipId(uint32_t id1, uint32_t id2, + uint32_t id3, uint32_t id4, uint32_t id5, uint32_t id6) +{ + for(int i = 0; i < chipInfoVector.size(); i++) + { + // Mandatory IDs + if (id1 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID1) || + id2 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID2)) + { + continue; + } + + // Optinal IDs + if (chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID3) == + ChipDb::paramNotDefValue) + { + return chipInfoVector[i]->getName(); + } + if (id3 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID3)) + continue; + + if (chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID4) == + ChipDb::paramNotDefValue) + { + return chipInfoVector[i]->getName(); + } + if (id4 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID4)) + continue; + + if (chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID5) == + ChipDb::paramNotDefValue) + { + return chipInfoVector[i]->getName(); + } + if (id5 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID5)) + continue; + + if (chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID6) == + ChipDb::paramNotDefValue) + { + return chipInfoVector[i]->getName(); + } + if (id6 != chipInfoVector[i]->getParam(SpiNorInfo::CHIP_PARAM_ID6)) + continue; + + return chipInfoVector[i]->getName(); + } + + return QString(); +} + +quint64 SpiNorDb::getChipParam(int chipIndex, int paramIndex) +{ + SpiNorInfo *ci = dynamic_cast(getChipInfo(chipIndex)); + + if (!ci || paramIndex < 0) + return 0; + + return ci->getParam(paramIndex); +} + +int SpiNorDb::setChipParam(int chipIndex, int paramIndex, + quint64 paramValue) +{ + SpiNorInfo *ci = dynamic_cast(getChipInfo(chipIndex)); + + if (!ci || paramIndex < 0) + return -1; + + return ci->setParam(paramIndex, paramValue); +} diff --git a/qt/spi_nor_db.h b/qt/spi_nor_db.h new file mode 100644 index 0000000..19beed0 --- /dev/null +++ b/qt/spi_nor_db.h @@ -0,0 +1,68 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef SPI_NOR_DB_H +#define SPI_NOR_DB_H + +#include "chip_db.h" +#include "spi_nor_info.h" + +#include +#include +#include +#include +#include + +class SpiNorDb : public ChipDb +{ +private: + QString dbFileName = "nando_spi_nor_db.csv"; + +protected: + QString getDbFileName() override; + ChipInfo *stringToChipInfo(const QString &s) override; + int chipInfoToString(ChipInfo *chipInfo, QString &s) override; + +public: + enum + { + CHIP_PARAM_NAME, + CHIP_PARAM_PAGE_SIZE, + CHIP_PARAM_BLOCK_SIZE, + CHIP_PARAM_TOTAL_SIZE, + CHIP_PARAM_PAGE_OFF, + CHIP_PARAM_READ_CMD, + CHIP_PARAM_READ_ID_CMD, + CHIP_PARAM_WRITE_CMD, + CHIP_PARAM_WRITE_EN_CMD, + CHIP_PARAM_ERASE_CMD, + CHIP_PARAM_STATUS_CMD, + CHIP_PARAM_BUSY_BIT, + CHIP_PARAM_BUSY_STATE, + CHIP_PARAM_ADDR_BYTES, // 新增:地址字节数 + CHIP_PARAM_ENTER_4BYTE, // 新增:进入4字节模式命令 + CHIP_PARAM_EXIT_4BYTE, // 新增:退出4字节模式命令 + CHIP_PARAM_FREQ, + CHIP_PARAM_ID1, + CHIP_PARAM_ID2, + CHIP_PARAM_ID3, + CHIP_PARAM_ID4, + CHIP_PARAM_ID5, + CHIP_PARAM_NUM + }; + + explicit SpiNorDb(); + virtual ~SpiNorDb(); + + ChipInfo *chipInfoGetByName(QString name); + int getIdByChipId(uint32_t id1, uint32_t id2, uint32_t id3, uint32_t id4, + uint32_t id5); + QString getNameByChipId(uint32_t id1, uint32_t id2, + uint32_t id3, uint32_t id4, uint32_t id5, uint32_t id6) override; + quint64 getChipParam(int chipIndex, int paramIndex); + int setChipParam(int chipIndex, int paramIndex, quint64 paramValue); +}; + +#endif // SPI_NOR_DB_H diff --git a/qt/spi_nor_db_dialog.cpp b/qt/spi_nor_db_dialog.cpp new file mode 100644 index 0000000..c35d61f --- /dev/null +++ b/qt/spi_nor_db_dialog.cpp @@ -0,0 +1,80 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nor_db_dialog.h" +#include "ui_spi_nor_db_dialog.h" + +#define HEADER_LONG_WIDTH 120 +#define HEADER_MED_WIDTH 110 +#define HEADER_SHORT_WIDTH 50 + +SpiNorDbDialog::SpiNorDbDialog(SpiNorDb *chipDb, QWidget *parent) : + QDialog(parent), ui(new Ui::SpiNorDbDialog), + chipDbTableModel(chipDb, parent) +{ + ui->setupUi(this); + +#ifdef Q_OS_WIN32 + QFont font("Courier New", 10); + ui->chipDbTableView->setFont(font); +#endif + + chipDbProxyModel.setSourceModel(&chipDbTableModel); + ui->chipDbTableView->setModel(&chipDbProxyModel); + ui->chipDbTableView->setColumnWidth(SpiNorDb::CHIP_PARAM_NAME, + HEADER_LONG_WIDTH); + ui->chipDbTableView->setColumnWidth(SpiNorDb::CHIP_PARAM_PAGE_SIZE, + HEADER_MED_WIDTH); + ui->chipDbTableView->setColumnWidth(SpiNorDb::CHIP_PARAM_BLOCK_SIZE, + HEADER_MED_WIDTH); + ui->chipDbTableView->setColumnWidth(SpiNorDb::CHIP_PARAM_TOTAL_SIZE, + HEADER_MED_WIDTH); + + for (int i = SpiNorDb::CHIP_PARAM_PAGE_OFF; + i <= SpiNorDb::CHIP_PARAM_FREQ; i++) + { + ui->chipDbTableView->setColumnWidth(i, HEADER_MED_WIDTH); + } + + connect(ui->addChipDbButton, SIGNAL(clicked()), this, + SLOT(slotAddChipDbButtonClicked())); + connect(ui->delChipDbButton, SIGNAL(clicked()), this, + SLOT(slotDelChipDbButtonClicked())); + connect(ui->okCancelButtonBox->button(QDialogButtonBox::Ok), + SIGNAL(clicked()), this, SLOT(slotOkButtonClicked())); + connect(ui->okCancelButtonBox->button(QDialogButtonBox::Cancel), + SIGNAL(clicked()), this, SLOT(slotCancelButtonClicked())); +} + +SpiNorDbDialog::~SpiNorDbDialog() +{ + delete ui; +} + +void SpiNorDbDialog::slotAddChipDbButtonClicked() +{ + chipDbTableModel.addRow(); +} + +void SpiNorDbDialog::slotDelChipDbButtonClicked() +{ + QModelIndexList selection = ui->chipDbTableView->selectionModel()-> + selectedRows(); + + if (!selection.count()) + return; + + chipDbTableModel.delRow(selection.at(0).row()); +} + +void SpiNorDbDialog::slotOkButtonClicked() +{ + chipDbTableModel.commit(); +} + +void SpiNorDbDialog::slotCancelButtonClicked() +{ + chipDbTableModel.reset(); +} diff --git a/qt/spi_nor_db_dialog.h b/qt/spi_nor_db_dialog.h new file mode 100644 index 0000000..de3c783 --- /dev/null +++ b/qt/spi_nor_db_dialog.h @@ -0,0 +1,36 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef SPI_NOR_DB_DALOG_H +#define SPI_NOR_DB_DALOG_H + +#include "spi_nor_db_table_model.h" +#include +#include + +namespace Ui { +class SpiNorDbDialog; +} + +class SpiNorDbDialog : public QDialog +{ + Q_OBJECT + + Ui::SpiNorDbDialog *ui; + SpiNorDbTableModel chipDbTableModel; + QSortFilterProxyModel chipDbProxyModel; + +public: + explicit SpiNorDbDialog(SpiNorDb *chipDb, QWidget *parent = nullptr); + ~SpiNorDbDialog(); + +private slots: + void slotAddChipDbButtonClicked(); + void slotDelChipDbButtonClicked(); + void slotOkButtonClicked(); + void slotCancelButtonClicked(); +}; + +#endif // SPI_NOR_DB_DALOG_H diff --git a/qt/spi_nor_db_dialog.ui b/qt/spi_nor_db_dialog.ui new file mode 100644 index 0000000..e7e60c7 --- /dev/null +++ b/qt/spi_nor_db_dialog.ui @@ -0,0 +1,208 @@ + + + SpiNorDbDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 1470 + 600 + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + SPI NOR数据库 + + + + + + QLayout::SetMaximumSize + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + 0 + 0 + + + + + 300 + 300 + + + + true + + + QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::SelectedClicked + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + Qt::ElideNone + + + QAbstractItemView::ScrollPerItem + + + QAbstractItemView::ScrollPerItem + + + true + + + true + + + 30 + + + 70 + + + 20 + + + 30 + + + + + + + + + + 0 + 0 + + + + + 40 + 40 + + + + + 40 + 40 + + + + + + + + + + + + + 0 + 0 + + + + + 40 + 40 + + + + + 40 + 40 + + + + - + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + okCancelButtonBox + accepted() + SpiNorDbDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + okCancelButtonBox + rejected() + SpiNorDbDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/qt/spi_nor_db_table_model.cpp b/qt/spi_nor_db_table_model.cpp new file mode 100644 index 0000000..025e6d8 --- /dev/null +++ b/qt/spi_nor_db_table_model.cpp @@ -0,0 +1,421 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nor_db_table_model.h" +#include + +#define CHIP_DB_TABLE_MODEL_MIN_CYCLES 1 +#define CHIP_DB_TABLE_MODEL_MAX_CYCLES 4 + +SpiNorDbTableModel::SpiNorDbTableModel(SpiNorDb *chipDb, + QObject *parent) : QAbstractTableModel(parent) +{ + this->chipDb = chipDb; +} + +int SpiNorDbTableModel::rowCount(const QModelIndex & /*parent*/) const +{ + return chipDb->size(); +} + +int SpiNorDbTableModel::columnCount(const QModelIndex & /*parent*/) const +{ + return SpiNorDb::CHIP_PARAM_NUM; +} + +QVariant SpiNorDbTableModel::data(const QModelIndex &index, int role) const +{ + int column; + QString paramStr; + + if (role != Qt::DisplayRole && role != Qt::EditRole) + return QVariant(); + + column = index.column(); + switch (column) + { + case SpiNorDb::CHIP_PARAM_NAME: + return chipDb->getChipName(index.row()); + case SpiNorDb::CHIP_PARAM_PAGE_SIZE: + chipDb->getHexStringFromParam(chipDb->getPageSize(index.row()), + paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_BLOCK_SIZE: + chipDb->getHexStringFromParam(chipDb->getBlockSize(index.row()), + paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_TOTAL_SIZE: + chipDb->getHexStringFromParam(chipDb->getTotalSize(index.row()), + paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_PAGE_OFF: + return (uint)chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_PAGE_OFF); + case SpiNorDb::CHIP_PARAM_READ_CMD: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_READ_CMD), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_READ_ID_CMD: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_READ_ID_CMD), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_WRITE_CMD: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_WRITE_CMD), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_WRITE_EN_CMD: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_WRITE_EN_CMD), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_ERASE_CMD: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_ERASE_CMD), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_STATUS_CMD: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_STATUS_CMD), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_BUSY_BIT: + return (uint)chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_BUSY_BIT); + case SpiNorDb::CHIP_PARAM_BUSY_STATE: + return (uint)chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_BUSY_STATE); + case SpiNorDb::CHIP_PARAM_ADDR_BYTES: + return (uint)chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_ADDR_BYTES); + case SpiNorDb::CHIP_PARAM_ENTER_4BYTE: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_ENTER_4BYTE), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_EXIT_4BYTE: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_EXIT_4BYTE), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_FREQ: + return (uint)chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_FREQ); + case SpiNorDb::CHIP_PARAM_ID1: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_ID1), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_ID2: + chipDb->getHexStringFromParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_ID2), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_ID3: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_ID3), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_ID4: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_ID4), paramStr); + return paramStr; + case SpiNorDb::CHIP_PARAM_ID5: + chipDb->getHexStringFromOptParam(chipDb->getChipParam(index.row(), + SpiNorInfo::CHIP_PARAM_ID5), paramStr); + return paramStr; + } + + return QVariant(); +} + +QVariant SpiNorDbTableModel::headerData(int section, + Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) + { + switch (section) + { + case SpiNorDb::CHIP_PARAM_NAME: return tr("Name"); + case SpiNorDb::CHIP_PARAM_PAGE_SIZE: return tr("Page size"); + case SpiNorDb::CHIP_PARAM_BLOCK_SIZE: return tr("Block size"); + case SpiNorDb::CHIP_PARAM_TOTAL_SIZE: return tr("Total size"); + case SpiNorDb::CHIP_PARAM_PAGE_OFF: return tr("Page off."); + case SpiNorDb::CHIP_PARAM_READ_CMD: return tr("Read com."); + case SpiNorDb::CHIP_PARAM_READ_ID_CMD: return tr("Read ID com."); + case SpiNorDb::CHIP_PARAM_WRITE_CMD: return tr("Write com."); + case SpiNorDb::CHIP_PARAM_WRITE_EN_CMD: return tr("Write en. com."); + case SpiNorDb::CHIP_PARAM_ERASE_CMD: return tr("Erase com."); + case SpiNorDb::CHIP_PARAM_STATUS_CMD: return tr("Status com."); + case SpiNorDb::CHIP_PARAM_BUSY_BIT: return tr("Busy bit"); + case SpiNorDb::CHIP_PARAM_BUSY_STATE: return tr("Busy bit state"); + case SpiNorDb::CHIP_PARAM_ADDR_BYTES: return tr("Addr bytes"); + case SpiNorDb::CHIP_PARAM_ENTER_4BYTE: return tr("Enter 4-byte com."); + case SpiNorDb::CHIP_PARAM_EXIT_4BYTE: return tr("Exit 4-byte com."); + case SpiNorDb::CHIP_PARAM_FREQ: return tr("Freq. (kHz)"); + case SpiNorDb::CHIP_PARAM_ID1: return tr("ID 1"); + case SpiNorDb::CHIP_PARAM_ID2: return tr("ID 2"); + case SpiNorDb::CHIP_PARAM_ID3: return tr("ID 3"); + case SpiNorDb::CHIP_PARAM_ID4: return tr("ID 4"); + case SpiNorDb::CHIP_PARAM_ID5: return tr("ID 5"); + } + } + + if (role == Qt::ToolTipRole) + { + switch (section) + { + case SpiNorDb::CHIP_PARAM_NAME: + return tr("Chip name"); + case SpiNorDb::CHIP_PARAM_PAGE_SIZE: + return tr("Page size in bytes"); + case SpiNorDb::CHIP_PARAM_BLOCK_SIZE: + return tr("Block size in bytes"); + case SpiNorDb::CHIP_PARAM_TOTAL_SIZE: + return tr("Total size in bytes"); + case SpiNorDb::CHIP_PARAM_PAGE_OFF: + return tr("Page offset in address"); + case SpiNorDb::CHIP_PARAM_READ_CMD: + return tr("Page read command"); + case SpiNorDb::CHIP_PARAM_READ_ID_CMD: + return tr("Read ID command"); + case SpiNorDb::CHIP_PARAM_WRITE_CMD: + return tr("Page write command"); + case SpiNorDb::CHIP_PARAM_WRITE_EN_CMD: + return tr("Write enable command"); + case SpiNorDb::CHIP_PARAM_ERASE_CMD: + return tr("Block erase command"); + case SpiNorDb::CHIP_PARAM_STATUS_CMD: + return tr("Read status command"); + case SpiNorDb::CHIP_PARAM_BUSY_BIT: + return tr("Busy bit number (0-7) in status register"); + case SpiNorDb::CHIP_PARAM_BUSY_STATE: + return tr("Busy bit active state (0/1)"); + case SpiNorDb::CHIP_PARAM_ADDR_BYTES: + return tr("Address bytes (3 or 4)"); + case SpiNorDb::CHIP_PARAM_ENTER_4BYTE: + return tr("Command to enter 4-byte address mode "); + case SpiNorDb::CHIP_PARAM_EXIT_4BYTE: + return tr("Command to exit 4-byte address mode "); + case SpiNorDb::CHIP_PARAM_FREQ: + return tr("Maximum supported SPI frequency in kHz"); + case SpiNorDb::CHIP_PARAM_ID1: + return tr("Chip ID 1st byte"); + case SpiNorDb::CHIP_PARAM_ID2: + return tr("Chip ID 2nd byte"); + case SpiNorDb::CHIP_PARAM_ID3: + return tr("Chip ID 3rd byte"); + case SpiNorDb::CHIP_PARAM_ID4: + return tr("Chip ID 4th byte"); + case SpiNorDb::CHIP_PARAM_ID5: + return tr("Chip ID 5th byte"); + } + } + + return QVariant(); +} + +Qt::ItemFlags SpiNorDbTableModel::flags (const QModelIndex &index) const +{ + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; +} + +bool SpiNorDbTableModel::setData(const QModelIndex &index, + const QVariant &value, int role) +{ + quint64 paramVal; + + if (role != Qt::EditRole) + return false; + + switch (index.column()) + { + case SpiNorDb::CHIP_PARAM_NAME: + chipDb->setChipName(index.row(), value.toString()); + return true; + case SpiNorDb::CHIP_PARAM_PAGE_SIZE: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + chipDb->setPageSize(index.row(), paramVal); + return true; + case SpiNorDb::CHIP_PARAM_BLOCK_SIZE: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + chipDb->setBlockSize(index.row(), paramVal); + return true; + case SpiNorDb::CHIP_PARAM_TOTAL_SIZE: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + chipDb->setTotalSize(index.row(), paramVal); + return true; + case SpiNorDb::CHIP_PARAM_PAGE_OFF: + if (chipDb->getParamFromString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_PAGE_OFF, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_READ_CMD: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_READ_CMD, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_READ_ID_CMD: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_READ_ID_CMD, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_WRITE_CMD: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_WRITE_CMD, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_WRITE_EN_CMD: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_WRITE_EN_CMD, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_ERASE_CMD: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_ERASE_CMD, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_STATUS_CMD: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_STATUS_CMD, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_BUSY_BIT: + if (chipDb->getParamFromString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0, 7)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_BUSY_BIT, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_BUSY_STATE: + if (chipDb->getParamFromString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0, 1)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_BUSY_STATE, + paramVal); + return true; + // 新增参数编辑处理 + case SpiNorDb::CHIP_PARAM_ADDR_BYTES: + // 解析整数输入,验证是否为3或4 + if (chipDb->getParamFromString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 3, 4)) // 地址字节数只能是3或4 + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_ADDR_BYTES, paramVal); + return true; + case SpiNorDb::CHIP_PARAM_ENTER_4BYTE: + // 解析十六进制命令,验证范围0x00-0xFF + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_ENTER_4BYTE, paramVal); + return true; + case SpiNorDb::CHIP_PARAM_EXIT_4BYTE: + // 解析十六进制命令,验证范围0x00-0xFF + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_EXIT_4BYTE, paramVal); + return true; + case SpiNorDb::CHIP_PARAM_FREQ: + if (chipDb->getParamFromString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFFFFFFFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_FREQ, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_ID1: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_ID1, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_ID2: + if (chipDb->getParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_ID2, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_ID3: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_ID3, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_ID4: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_ID4, + paramVal); + return true; + case SpiNorDb::CHIP_PARAM_ID5: + if (chipDb->getOptParamFromHexString(value.toString(), paramVal)) + return false; + if (!chipDb->isOptParamValid(paramVal, 0x00, 0xFF)) + return false; + chipDb->setChipParam(index.row(), SpiNorInfo::CHIP_PARAM_ID5, + paramVal); + return true; + } + + return false; +} + +void SpiNorDbTableModel::addRow() +{ + SpiNorInfo *chipInfo = new SpiNorInfo(); + + beginResetModel(); + chipDb->addChip(chipInfo); + endResetModel(); +} + +void SpiNorDbTableModel::delRow(int index) +{ + beginResetModel(); + chipDb->delChip(index); + endResetModel(); +} + +void SpiNorDbTableModel::commit() +{ + chipDb->commit(); +} + +void SpiNorDbTableModel::reset() +{ + beginResetModel(); + chipDb->reset(); + endResetModel(); +} + diff --git a/qt/spi_nor_db_table_model.h b/qt/spi_nor_db_table_model.h new file mode 100644 index 0000000..23326fa --- /dev/null +++ b/qt/spi_nor_db_table_model.h @@ -0,0 +1,34 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef SPI_NOR_DB_TABLE_MODEL_H +#define SPI_NOR_DB_TABLE_MODEL_H + +#include "spi_nor_db.h" +#include + +class SpiNorDbTableModel : public QAbstractTableModel +{ + Q_OBJECT + + SpiNorDb *chipDb; + +public: + explicit SpiNorDbTableModel(SpiNorDb *chipDb, QObject *parent = nullptr); + int rowCount(const QModelIndex & /*parent*/) const override; + int columnCount(const QModelIndex & /*parent*/) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) + const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + bool setData(const QModelIndex &index, const QVariant &value, + int role = Qt::EditRole) override; + void addRow(); + void delRow(int index); + void commit(); + void reset(); +}; + +#endif // SPI_NOR_DB_TABLE_MODEL_H diff --git a/qt/spi_nor_info.cpp b/qt/spi_nor_info.cpp new file mode 100644 index 0000000..75dc8ad --- /dev/null +++ b/qt/spi_nor_info.cpp @@ -0,0 +1,74 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#include "spi_nor_info.h" + +typedef struct __attribute__((__packed__)) +{ + uint8_t page_offset; + uint8_t read_cmd; + uint8_t read_id_cmd; + uint8_t write_cmd; + uint8_t write_en_cmd; + uint8_t erase_cmd; + uint8_t status_cmd; + uint8_t busy_bit; + uint8_t busy_state; + uint8_t addr_bytes; // 新增:地址字节数 (3或4) + uint8_t enter_4byte_cmd; // 新增:进入4字节模式命令 + uint8_t exit_4byte_cmd; // 新增:退出4字节模式命令 + uint32_t freq; +} SpiNorConf; + +SpiNorInfo::SpiNorInfo() +{ + hal = CHIP_HAL_SPI_NOR; +} + +SpiNorInfo::~SpiNorInfo() +{ +} + +const QByteArray &SpiNorInfo::getHalConf() +{ + SpiNorConf conf; + + conf.page_offset = static_cast(params[CHIP_PARAM_PAGE_OFF]); + conf.read_cmd = static_cast(params[CHIP_PARAM_READ_CMD]); + conf.read_id_cmd = static_cast(params[CHIP_PARAM_READ_ID_CMD]); + conf.write_cmd = static_cast(params[CHIP_PARAM_WRITE_CMD]); + conf.write_en_cmd = static_cast(params[CHIP_PARAM_WRITE_EN_CMD]); + conf.erase_cmd = static_cast(params[CHIP_PARAM_ERASE_CMD]); + conf.status_cmd = static_cast(params[CHIP_PARAM_STATUS_CMD]); + conf.busy_bit = static_cast(params[CHIP_PARAM_BUSY_BIT]); + conf.busy_state = static_cast(params[CHIP_PARAM_BUSY_STATE]); + conf.addr_bytes = static_cast(params[CHIP_PARAM_ADDR_BYTES]); // 地址字节数参数 + conf.enter_4byte_cmd = static_cast(params[CHIP_PARAM_ENTER_4BYTE]); // 进入4字节模式命令参数 + conf.exit_4byte_cmd = static_cast(params[CHIP_PARAM_EXIT_4BYTE]); // 退出4字节模式命令参数 + conf.freq = params[CHIP_PARAM_FREQ]; + + halConf.clear(); + halConf.append(reinterpret_cast(&conf), sizeof(conf)); + + return halConf; +} + +quint64 SpiNorInfo::getParam(uint32_t num) +{ + if (num >= CHIP_PARAM_NUM) + return 0; + + return params[num]; +} + +int SpiNorInfo::setParam(uint32_t num, quint64 value) +{ + if (num >= CHIP_PARAM_NUM) + return -1; + + params[num] = value; + + return 0; +} diff --git a/qt/spi_nor_info.h b/qt/spi_nor_info.h new file mode 100644 index 0000000..c8e9e0f --- /dev/null +++ b/qt/spi_nor_info.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2020 NANDO authors + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3. + */ + +#ifndef SPI_NOR_INFO_H +#define SPI_NOR_INFO_H + +#include "chip_info.h" + +class SpiNorInfo : public ChipInfo +{ +public: + enum + { + CHIP_PARAM_PAGE_OFF, + CHIP_PARAM_READ_CMD, + CHIP_PARAM_READ_ID_CMD, + CHIP_PARAM_WRITE_CMD, + CHIP_PARAM_WRITE_EN_CMD, + CHIP_PARAM_ERASE_CMD, + CHIP_PARAM_STATUS_CMD, + CHIP_PARAM_BUSY_BIT, + CHIP_PARAM_BUSY_STATE, + CHIP_PARAM_ADDR_BYTES, // 新增:地址字节数 + CHIP_PARAM_ENTER_4BYTE, // 新增:进入4字节模式命令 + CHIP_PARAM_EXIT_4BYTE, // 新增:退出4字节模式命令 + CHIP_PARAM_FREQ, + CHIP_PARAM_ID1, + CHIP_PARAM_ID2, + CHIP_PARAM_ID3, + CHIP_PARAM_ID4, + CHIP_PARAM_ID5, + CHIP_PARAM_ID6, + CHIP_PARAM_NUM, + }; + +private: + QByteArray halConf; + quint64 params[CHIP_PARAM_NUM] = {}; + +public: + SpiNorInfo(); + virtual ~SpiNorInfo(); + const QByteArray &getHalConf() override; + quint64 getParam(uint32_t num) override; + int setParam(uint32_t num, quint64 value) override; +}; + +#endif // SPI_NOR_INFO_H diff --git a/qt/version.h b/qt/version.h index 8538302..c70c580 100644 --- a/qt/version.h +++ b/qt/version.h @@ -6,6 +6,6 @@ #ifndef VERSION_H #define VERSION_H -#define SW_VERSION "3.5.0" +#define SW_VERSION "3.6.0" #endif // VERSION_H