リーズナブルFPGAシリーズのTang Nano 9Kを使ってTFカードスロットにアクセスしました(その2。ソフト設計編)
今回はソフト設計編になります。
- リーズナブルFPGAシリーズのTang Nano 9Kを使ってTFカードスロットにアクセスしました(その2。ソフト設計編)
- exampleのソフトについて
- FatFsドライバ
- firmware.cの修正
- 参考にさせていただいたサイト
exampleのソフトについて
まずはexampleの構造から説明します。 exampleをダウンロードすると、以下の様な構造になっています。(ソフト開発に必要なディレクトリのみを挙げています)
root +- fw +- fw-brom ←ココはブートROMコード。今回は触らず。(修正したらRTL埋め込みを変更する) +- fw-flash ← ボード内蔵のフラッシュへ書き込むプログラムが格納 +- sw +- pico-programmer.py ← ボード内蔵のフラッシュメモリを書き換えるpythonプログラム
前回、記載しましたようにboot programはRTLに埋め込まれています。このプログラムのソースコードはfw-brom内に格納されています。今回このプログラムは触りません。 またboot programにはUARTから受信したデータをフラッシュへ書き込むプログラムが仕込まれています。(ISPプログラムとwikiでは呼んでいます) このプログラムを呼び出し、UARTを介して通信するのがpythonプログラム "pico-programmer.py"になります。
フラッシュメモリへファームウェアの書き込み方法
書き込みプログラムはpythonプログラムなので、pythonをインストールする必要があります。
TangNano9KとPCはUSBを介したCOMポートで繋がります。wsl2からだとCOMポートを繋げるのに苦労しそうだったので、書き込みはwindowsから行う事にします。
windows用のpythonをインストールします。
適当にググればpythonのインストール方法は出てきますが、wikiのREADMEに記載があるようにpythonのバージョンは3.6以上でないといけません。
私はインストール時点の最新版3.11.1をインストールしました。
以下からダウンロードしました。
Welcome to Python.org
本書き込みプログラムは、初期状態でインストールされないモジュールpyserialが必要です。
以下の様なコマンドを打ってインストールします。
python -m pip install pyserial
他に必要なモジュールがあるかもしれません(忘れてしまいました)。うまく動かなかったら随時インストールしましょう。足りないモジュールはpythonから、このモジュールがありませんと言われるので、雰囲気でモジュールは追加できます。
TANG Nano 9KボードをUSB経由でPCと接続し、GOWIN FPGA Designerからbit streamを書き込むと、本記事の冒頭の様なメニューがHDMI表示されます。
この状態で、ファームウェア書き換えプログラム を動作させます
DOS窓またはpower shellを立ち上げて sw ディレクトリが見えているフォルダへ移動します。
python sw/pico-programmer.py example-fw-flash.v COM14
COMの番号は環境によって異なりますので、デバイスマネージャでCOMの何番で繋がっているか確認してください。
本コマンドでsw/example-fw-flash.vをフラッシュメモリへ書き込めます。
Read program with xxx bytes - Waiting for reset - ... と出たら,S1ボタンを1sec以下で押して離します。 Total sectors 3 Total pages 46 Flashing 1 / 3 Flashing 2 / 3 Flashing 3 / 3 と出て書き込みが完了します
FatFsドライバ
FatFsの展開
ラズパイPicoでFatFsを使う - Qiita
本記事を参考に作成します。
※FatFsのページ
http://elm-chan.org/fsw/ff/00index_e.html
ここからFatfs本体のff15.zip及びサンプルの"ffsample.zip" をダウンロードし展開します。
fw-flashの下にfatfsというディレクトリを作成して
fw --- fw-flash -- fatfs
ff15/source ディレクトリ内のファイルをfatfs内へ入れます。
次に、ffsample.zipファイル内から\stm32\mmc_stm32f1_spi.cファイルを取り出し、先のfatfs内へmmc_picorv32_spi.cというファイル名でコピーします。
以下のファイルが置かれていると思います。
fw --- fw-flash --- fatfs |-- 00history.txt |-- 00readme.txt |-- diskio.c |-- diskio.h |-- ff.c |-- ff.h |-- ffconf.h |-- ffsystem.c |-- ffunicode.c |-- mmc_picorv32_spi.c
diskio.cは不要なので削除してください。
picorv32用のコードへ修正
fatfsを動作させるのに必要な関数は上記の参考ページに記載されていますが、その修正すべき該当関数を修正します。
mmc_picorv32_spi.c の14行目から208行目までを以下のコードへ置き替えます。
#include "spi.h" #define FCLK_FAST() { } #define FCLK_SLOW() { } #define CS_HIGH() { SPI_SD->CSout = 1; /* HIGH */ } #define CS_LOW() { SPI_SD->CSout = 0; /* LOW */ } #define MMC_CD 1 /* Card detect (yes:true, no:false, default:true) */ #define MMC_WP 0 /* Write protected (yes:true, no:false, default:false) */ volatile int conter1; void Delay( int ms ) { // 25.2MHz //for( conter1=0; conter1<25200*ms ; conter1++ ) ; for( conter1=0; conter1<25.2*ms ; conter1++ ) ; } /*-------------------------------------------------------------------------- Module Private Functions ---------------------------------------------------------------------------*/ #include "ff.h" /* Obtains integer types */ #include "diskio.h" /* Declarations of disk functions */ /* MMC card type flags (MMC_GET_TYPE) */ #define CT_MMC3 0x01 /* MMC ver 3 */ #define CT_MMC4 0x02 /* MMC ver 4+ */ #define CT_MMC 0x03 /* MMC */ #define CT_SDC1 0x02 /* SDC ver 1 */ #define CT_SDC2 0x04 /* SDC ver 2+ */ #define CT_SDC 0x0C /* SDC */ #define CT_BLOCK 0x10 /* Block addressing */ /* MMC/SD command */ #define CMD0 (0) /* GO_IDLE_STATE */ #define CMD1 (1) /* SEND_OP_COND (MMC) */ #define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */ #define CMD8 (8) /* SEND_IF_COND */ #define CMD9 (9) /* SEND_CSD */ #define CMD10 (10) /* SEND_CID */ #define CMD12 (12) /* STOP_TRANSMISSION */ #define ACMD13 (0x80+13) /* SD_STATUS (SDC) */ #define CMD16 (16) /* SET_BLOCKLEN */ #define CMD17 (17) /* READ_SINGLE_BLOCK */ #define CMD18 (18) /* READ_MULTIPLE_BLOCK */ #define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */ #define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */ #define CMD24 (24) /* WRITE_BLOCK */ #define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */ #define CMD32 (32) /* ERASE_ER_BLK_START */ #define CMD33 (33) /* ERASE_ER_BLK_END */ #define CMD38 (38) /* ERASE */ #define CMD55 (55) /* APP_CMD */ #define CMD58 (58) /* READ_OCR */ static volatile DSTATUS Stat = STA_NOINIT; /* Physical drive status */ static volatile UINT Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */ static BYTE CardType; /* Card type flags */ /*-----------------------------------------------------------------------*/ /* SPI controls (Platform dependent) */ /*-----------------------------------------------------------------------*/ /* Initialize MMC interface */ static void init_spi (void) { // SPI clock 25.2/4=6.3Mbps /* CS# */ CS_HIGH(); /* Set CS# high */ #if 0 for (Timer1 = 10; Timer1; ) ; /* 10ms */ #else Delay( 10 ); #endif } /* Exchange a byte */ static BYTE xchg_spi ( BYTE dat /* Data to send */ ) { SPI_SD->txdata = dat; //while( !(SPI_SD->status&0x1) ) ; // SPI_BSY=1になるのを待つ while( !(SPI_SD->status&0x8) ) ; // SPI_RNE=1(rxfifo not empty)になるまで待つ return (BYTE)SPI_SD->rxdata; } /* Receive multiple byte */ static void rcvr_spi_multi ( BYTE *buff, /* Pointer to data buffer */ UINT btr /* Number of bytes to receive (even number) */ ) { //spi_read_blocking( SPIDEV, 0xff, buff, btr ); UINT rx_remaining, tx_remaining; UINT i; rx_remaining = btr; tx_remaining = btr; while(rx_remaining || tx_remaining) { if(tx_remaining && (!(SPI_SD->status & 0x4)) ) { // txfifoがフルにならない間 SPI_SD->txdata = 0xff; // dummy tx --tx_remaining; } if(rx_remaining && (SPI_SD->status & 0x8)) { // rxfifoにデータ有 *buff++ = (uint8_t) SPI_SD->rxdata; --rx_remaining; } } } /* Send multiple byte */ static void xmit_spi_multi ( const BYTE *buff, /* Pointer to the data */ UINT btx /* Number of bytes to send (even number) */ ) { volatile uint8_t dummy_receive; //spi_write_blocking( SPIDEV, buff, btx ); // btn分 送信する(rxdataは無視する) UINT i; for(i=0; i<btx; i++) { while(SPI_SD->status & 0x4) ; // txfifoがフルの時,wait SPI_SD->txdata = *buff++; } // 送信がはけるまで待つ(=BSY=0) while(SPI_SD->status & 0x1) ; // rxfifo がemptyになるようにdummy read while(SPI_SD->status & 0x8) // rxfifo not empty dummy_receive = (uint8_t) SPI_SD->rxdata; }
spi.hをincludeしていますが、以下の内容でspi.hという名前でfatfs配下に新規作成します。これは作成したSPIモジュールのレジスタ定義とアドレスになります。
#include <stdint.h> typedef struct { volatile uint32_t control0; volatile uint32_t control1; volatile uint32_t txdata; volatile uint32_t rxdata; volatile uint32_t status; volatile uint32_t CSout; } PICOSPI; #define SPI_SD ((PICOSPI*)0x84000000)
fw-flash/fatfs/Makefileの追加
fatfsのバイナリ作成用にMakefileを作成します。
オリジナルのfw-flash/Makefileをベースに修正します。
- RISCV_PATHの変更
- LDFLAGSから-nostdlibの削除
- linkerスクリプトの呼び出しの削除
- *.oの作成のみ
Makefileの作成方法がイマイチ分からなかった為、合ってるかどうかは分かりませんが、以下の様に作成しました。
PROJ_NAME=fw-flash DEBUG=no BENCH=no MULDIV=no COMPRESSED=no SRCS = $(wildcard *.c) \ $(wildcard *.S) LDSCRIPT = ./linker_flash.ld RISCV_NAME ?= riscv-none-embed RISCV_PATH ?= ~/riscv/riscv-none-embed-gcc/8.2.0-3.1 MABI=ilp32 MARCH := rv32i ifeq ($(MULDIV),yes) MARCH := $(MARCH)m endif ifeq ($(COMPRESSED),yes) MARCH := $(MARCH)ac endif CFLAGS += -march=$(MARCH) -mabi=$(MABI) -ffunction-sections -fdata-sections LDFLAGS += -march=$(MARCH) -mabi=$(MABI) -Wl,--gc-sections ifeq ($(DEBUG),yes) CFLAGS += -g3 -O0 endif ifeq ($(DEBUG),no) CFLAGS += -g -O3 endif ifeq ($(BENCH),yes) CFLAGS += -fno-inline endif RISCV_CLIB=$(RISCV_PATH)/$(RISCV_NAME)/lib/$(MARCH)/$(MABI)/ RISCV_OBJCOPY = $(RISCV_PATH)/bin/$(RISCV_NAME)-objcopy RISCV_OBJDUMP = $(RISCV_PATH)/bin/$(RISCV_NAME)-objdump RISCV_CC = $(RISCV_PATH)/bin/$(RISCV_NAME)-gcc CFLAGS += -MD -fstrict-volatile-bitfields #LDFLAGS += -nostdlib -lgcc -mcmodel=medany -nostartfiles -ffreestanding -Wl,-Bstatic,-T,$(LDSCRIPT),-Map,$(OBJDIR)/$(PROJ_NAME).map,--print-memory-usage LDFLAGS += -lgcc -mcmodel=medany -nostartfiles -ffreestanding -Wl,-Bstatic OBJDIR = build OBJS := $(SRCS) OBJS := $(OBJS:.c=.o) OBJS := $(OBJS:.cpp=.o) OBJS := $(OBJS:.S=.o) OBJS := $(addprefix $(OBJDIR)/,$(OBJS)) SUBOBJ := $(addprefix $(OBJDIR)/,$(SUBDIRS)) SUBOBJ := $(addsuffix /*.o,$(SUBOBJ)) export RISCV_CC CFLAGS LDFLAGS OBJDIR #all: $(SUBDIRS) $(OBJDIR)/$(PROJ_NAME).elf $(OBJDIR)/$(PROJ_NAME).hex $(OBJDIR)/$(PROJ_NAME).asm $(OBJDIR)/$(PROJ_NAME).v all: $(OBJS) $(SUBDIRS): ECHO make -C $@ ECHO: @echo $(SUBDIRS) $(OBJDIR)/%.o: %.c mkdir -p $(dir $@) $(RISCV_CC) -c $(CFLAGS) $(INC) -o $@ $^ $(OBJDIR)/%.o: %.cpp mkdir -p $(dir $@) $(RISCV_CC) -c $(CFLAGS) $(INC) -o $@ $^ $(OBJDIR)/%.o: %.S mkdir -p $(dir $@) $(RISCV_CC) -c $(CFLAGS) -o $@ $^ -D__ASSEMBLY__=1 $(OBJDIR): mkdir -p $@ clean: rm -f $(OBJDIR)/$(PROJ_NAME).elf rm -f $(OBJDIR)/$(PROJ_NAME).hex rm -f $(OBJDIR)/$(PROJ_NAME).map rm -f $(OBJDIR)/$(PROJ_NAME).v rm -f $(OBJDIR)/$(PROJ_NAME).asm find $(OBJDIR) -type f -name '*.d' -print0 | xargs -0 -r rm find $(OBJDIR) -type f -name '*.o' -print0 | xargs -0 -r rm .SECONDARY: $(OBJS)
ffconf.hの修正
LFNを扱うためと、timestamp機能が無いので、以下のdefineへ修正します。
#define FF_USE_LFN 3 #define FF_FS_NORTC 1 ←固定日付 #define FF_NORTC_MON 2 ←固定日付の月指定 #define FF_NORTC_MDAY 12←固定日付の日指定 #define FF_NORTC_YEAR 2023←固定日付の年指定
fatfs配下でmakeコマンドをたたけば、build配下に以下のファイルが生成されてるはずです。
ff.d ff.o ffsystem.d ffsystem.o ffunicode.d ffunicode.o mmc_picorv32_spi.d mmc_picorv32_spi.o
firmware.cの修正
fw-flash/linker_flash.ldの修正
exampleではstackサイズが1KBとなっていますが、このサイズだとfatfsの関数呼び出し時にハングアップする事があったので、倍の2KBへ変更します。
linker_flash.ld内の_stack_size定義を以下の様に修正しました。
cpp _stack_size = DEFINED(_stack_size) ? _stack_size : 2k;
fw-flash/Makefileの修正
こちらのMakefileも修正しています。
LDFLAGSから-nostdlibの削除、sprintfを使っているので--specs=nano.specs --specs=nosys.specsが必要でした。
fw-flash/ 配下のMakefile
PROJ_NAME=fw-flash DEBUG=no BENCH=no MULDIV=no COMPRESSED=no SRCS = $(wildcard *.c) \ $(wildcard *.S) LDSCRIPT = ./linker_flash.ld SUBDIRS = fatfs RISCV_NAME ?= riscv-none-embed RISCV_PATH ?= ~/riscv/riscv-none-embed-gcc/8.2.0-3.1 MABI=ilp32 MARCH := rv32i ifeq ($(MULDIV),yes) MARCH := $(MARCH)m endif ifeq ($(COMPRESSED),yes) MARCH := $(MARCH)ac endif CFLAGS += -march=$(MARCH) -mabi=$(MABI) -ffunction-sections -fdata-sections LDFLAGS += -march=$(MARCH) -mabi=$(MABI) -Wl,--gc-sections ifeq ($(DEBUG),yes) CFLAGS += -g3 -O0 endif ifeq ($(DEBUG),no) CFLAGS += -g -O3 endif ifeq ($(BENCH),yes) CFLAGS += -fno-inline endif RISCV_CLIB=$(RISCV_PATH)/$(RISCV_NAME)/lib/$(MARCH)/$(MABI)/ RISCV_OBJCOPY = $(RISCV_PATH)/bin/$(RISCV_NAME)-objcopy RISCV_OBJDUMP = $(RISCV_PATH)/bin/$(RISCV_NAME)-objdump RISCV_CC = $(RISCV_PATH)/bin/$(RISCV_NAME)-gcc CFLAGS += -MD -fstrict-volatile-bitfields #LDFLAGS += -nostdlib -lgcc -mcmodel=medany -nostartfiles -ffreestanding -Wl,-Bstatic,-T,$(LDSCRIPT),-Map,$(OBJDIR)/$(PROJ_NAME).map,--print-memory-usage LDFLAGS += -lgcc -mcmodel=medany -nostartfiles -ffreestanding --specs=nano.specs --specs=nosys.specs -Wl,-Bstatic,-T,$(LDSCRIPT),-Map,$(OBJDIR)/$(PROJ_NAME).map,--print-memory-usage OBJDIR = build OBJS := $(SRCS) OBJS := $(OBJS:.c=.o) OBJS := $(OBJS:.cpp=.o) OBJS := $(OBJS:.S=.o) OBJS := $(addprefix $(OBJDIR)/,$(OBJS)) #SUBOBJ := $(addprefix $(OBJDIR)/,$(SUBDIRS)) SUBOBJ := $(addsuffix /$(OBJDIR),$(SUBDIRS)) SUBOBJ := $(addsuffix /*.o,$(SUBOBJ)) export RISCV_CC CFLAGS LDFLAGS OBJDIR all: $(SUBDIRS) $(OBJDIR)/$(PROJ_NAME).elf $(OBJDIR)/$(PROJ_NAME).hex $(OBJDIR)/$(PROJ_NAME).asm $(OBJDIR)/$(PROJ_NAME).v $(SUBDIRS): ECHO make -C $@ ECHO: @echo $(SUBDIRS) #$(OBJDIR)/%.elf: $(OBJS) | $(OBJDIR) # $(RISCV_CC) $(CFLAGS) -o $@ $^ $(SUBOBJ) $(LDFLAGS) $(LIBS) $(OBJDIR)/%.elf: $(OBJS) | $(OBJDIR) $(RISCV_CC) $(CFLAGS) -o $@ $^ $(SUBOBJ) $(LDFLAGS) $(LIBS) #$(OBJDIR)/%.elf: $(SUBDIRS)/$(OBJDIR) $(OBJDIR)/%.elf: $(SUBOBJ) %.hex: %.elf $(RISCV_OBJCOPY) -O ihex $^ $@ %.bin: %.elf $(RISCV_OBJCOPY) -O binary $^ $@ %.v: %.elf $(RISCV_OBJCOPY) -O verilog $^ $@ %.asm: %.elf $(RISCV_OBJDUMP) -S -d $^ > $@ $(OBJDIR)/%.o: %.c mkdir -p $(dir $@) $(RISCV_CC) -c $(CFLAGS) $(INC) -o $@ $^ $(OBJDIR)/%.o: %.cpp mkdir -p $(dir $@) $(RISCV_CC) -c $(CFLAGS) $(INC) -o $@ $^ $(OBJDIR)/%.o: %.S mkdir -p $(dir $@) $(RISCV_CC) -c $(CFLAGS) -o $@ $^ -D__ASSEMBLY__=1 $(OBJDIR): mkdir -p $@ clean: rm -f $(OBJDIR)/$(PROJ_NAME).elf rm -f $(OBJDIR)/$(PROJ_NAME).hex rm -f $(OBJDIR)/$(PROJ_NAME).map rm -f $(OBJDIR)/$(PROJ_NAME).v rm -f $(OBJDIR)/$(PROJ_NAME).asm find $(OBJDIR) -type f -name '*.d' -print0 | xargs -0 -r rm find $(OBJDIR) -type f -name '*.o' -print0 | xargs -0 -r rm .SECONDARY: $(OBJS)
firmware.cのコード修正
これでfatfsの関数は使えるようになったので、 firmware.cのmain関数から呼び出すだけです。
メニュー"z"からSDカードアクセスが可能なようにしました。
root dirからファイル一覧を取得し、指定されたファイルを読みだして、UART(HDMI画面)へ出力するものです。
以下の3ファイルをSDカードのroot dirへ置きました。
file1.txt this is file1 file2.txt this is file2 test.txt ABCDEFGHTJKLMNOPQRSTUVWXYZ abcdefghtjklmnopqrstuvwxyz 0123456789
以下の様なコードへfirmware.cを修正しました。
#include <stdint.h> #include <stdbool.h> #include <stdio.h> #include "fatfs/ff.h" #include "fatfs/diskio.h" #include "fatfs/spi.h" //#include <string.h> // a pointer to this is a null pointer, but the compiler does not // know that because "sram" is a linker symbol from sections.lds. extern uint32_t sram; typedef struct { volatile uint32_t DATA; volatile uint32_t CLKDIV; } PICOUART; typedef struct { volatile uint32_t OUT; volatile uint32_t IN; volatile uint32_t OE; } PICOGPIO; typedef struct { union { volatile uint32_t REG; volatile uint16_t IOW; struct { volatile uint8_t IO; volatile uint8_t OE; volatile uint8_t CFG; volatile uint8_t EN; }; }; } PICOQSPI; #define QSPI0 ((PICOQSPI*)0x81000000) #define GPIO0 ((PICOGPIO*)0x82000000) #define UART0 ((PICOUART*)0x83000000) #define FLASHIO_ENTRY_ADDR ((void *)0x80000054) void (*spi_flashio)(uint8_t *pdata, int length, int wren) = FLASHIO_ENTRY_ADDR; // for fat test char sbuff[64]; #define DEF_FATBUFF 1024 char buff_fattest[ DEF_FATBUFF ]; char file_name[10][64]; // 最大file数=10 // end int putchar(int c) { if (c == '\n') UART0->DATA = '\r'; UART0->DATA = c; return c; } void print(const char *p) { while (*p) putchar(*(p++)); } void print_hex(uint32_t v, int digits) { for (int i = 7; i >= 0; i--) { char c = "0123456789abcdef"[(v >> (4*i)) & 15]; if (c == '0' && i >= digits) continue; putchar(c); digits = i; } } void print_dec(uint32_t v) { if (v >= 100) { print(">=100"); return; } if (v >= 90) { putchar('9'); v -= 90; } else if (v >= 80) { putchar('8'); v -= 80; } else if (v >= 70) { putchar('7'); v -= 70; } else if (v >= 60) { putchar('6'); v -= 60; } else if (v >= 50) { putchar('5'); v -= 50; } else if (v >= 40) { putchar('4'); v -= 40; } else if (v >= 30) { putchar('3'); v -= 30; } else if (v >= 20) { putchar('2'); v -= 20; } else if (v >= 10) { putchar('1'); v -= 10; } if (v >= 9) { putchar('9'); v -= 9; } else if (v >= 8) { putchar('8'); v -= 8; } else if (v >= 7) { putchar('7'); v -= 7; } else if (v >= 6) { putchar('6'); v -= 6; } else if (v >= 5) { putchar('5'); v -= 5; } else if (v >= 4) { putchar('4'); v -= 4; } else if (v >= 3) { putchar('3'); v -= 3; } else if (v >= 2) { putchar('2'); v -= 2; } else if (v >= 1) { putchar('1'); v -= 1; } else putchar('0'); } char getchar_prompt(char *prompt) { int32_t c = -1; uint32_t cycles_begin, cycles_now, cycles; __asm__ volatile ("rdcycle %0" : "=r"(cycles_begin)); if (prompt) print(prompt); // if (prompt) // GPIO0->OUT = ~0; // reg_leds = ~0; while (c == -1) { __asm__ volatile ("rdcycle %0" : "=r"(cycles_now)); cycles = cycles_now - cycles_begin; if (cycles > 12000000) { if (prompt) print(prompt); cycles_begin = cycles_now; // if (prompt) // GPIO0->OUT = ~GPIO0->OUT; // reg_leds = ~reg_leds; } c = UART0->DATA; } // if (prompt) // GPIO0->OUT = 0; // reg_leds = 0; return c; } //char getchar() char getchar_wrap() { return getchar_prompt(0); } #define QSPI_REG_CRM 0x00100000 #define QSPI_REG_DSPI 0x00400000 void cmd_set_crm(int on) { if (on) { QSPI0->REG |= QSPI_REG_CRM; } else { QSPI0->REG &= ~QSPI_REG_CRM; } } int cmd_get_crm() { return QSPI0->REG & QSPI_REG_CRM; } void cmd_set_dspi(int on) { if (on) { QSPI0->REG |= QSPI_REG_DSPI; } else { QSPI0->REG &= ~QSPI_REG_DSPI; } } int cmd_get_dspi() { return QSPI0->REG & QSPI_REG_DSPI; } void cmd_read_flash_id() { int pre_dspi = cmd_get_dspi(); cmd_set_dspi(0); uint8_t buffer[4] = { 0x9F, /* zeros */ }; spi_flashio(buffer, 4, 0); for (int i = 1; i <= 3; i++) { putchar(' '); print_hex(buffer[i], 2); } putchar('\n'); cmd_set_dspi(pre_dspi); } // -------------------------------------------------------- uint32_t cmd_benchmark(bool verbose, uint32_t *instns_p) { uint8_t data[256]; uint32_t *words = (void*)data; uint32_t x32 = 314159265; uint32_t cycles_begin, cycles_end; uint32_t instns_begin, instns_end; __asm__ volatile ("rdcycle %0" : "=r"(cycles_begin)); __asm__ volatile ("rdinstret %0" : "=r"(instns_begin)); for (int i = 0; i < 20; i++) { for (int k = 0; k < 256; k++) { x32 ^= x32 << 13; x32 ^= x32 >> 17; x32 ^= x32 << 5; data[k] = x32; } for (int k = 0, p = 0; k < 256; k++) { if (data[k]) data[p++] = k; } for (int k = 0, p = 0; k < 64; k++) { x32 = x32 ^ words[k]; } } __asm__ volatile ("rdcycle %0" : "=r"(cycles_end)); __asm__ volatile ("rdinstret %0" : "=r"(instns_end)); if (verbose) { print("Cycles: 0x"); print_hex(cycles_end - cycles_begin, 8); putchar('\n'); print("Instns: 0x"); print_hex(instns_end - instns_begin, 8); putchar('\n'); print("Chksum: 0x"); print_hex(x32, 8); putchar('\n'); } if (instns_p) *instns_p = instns_end - instns_begin; return cycles_end - cycles_begin; } void cmd_benchmark_all() { uint32_t instns = 0; print("default "); cmd_set_dspi(0); cmd_set_crm(0); print(": "); print_hex(cmd_benchmark(false, &instns), 8); putchar('\n'); print("dspi-"); print_dec(0); print(" "); cmd_set_dspi(1); print(": "); print_hex(cmd_benchmark(false, &instns), 8); putchar('\n'); print("dspi-crm-"); print_dec(0); print(" "); cmd_set_crm(1); print(": "); print_hex(cmd_benchmark(false, &instns), 8); putchar('\n'); print("instns : "); print_hex(instns, 8); putchar('\n'); } volatile int i; // -------------------------------------------------------- #define CLK_FREQ 25175000 #define UART_BAUD 115200 int fat_test_init( void ) { DSTATUS ret; int result = 0; ret = disk_initialize( 0 ); if( ret & STA_NOINIT ) { result = -1; } return result; } int cat_file( char *filename, char *buff, int bsize ) { FRESULT ret; FATFS fs; FIL fil; UINT rdsz ; ret = f_mount( &fs, "", 0 ); if( ret != FR_OK ) { return -1; } ret = f_open( &fil, filename, FA_READ ); if( ret != FR_OK ) { return -2; } ret = f_read( &fil, buff, (UINT)bsize, &rdsz ); if( ret != FR_OK ) { return -3; } buff[rdsz]=0; print(buff); f_close( &fil ); return (int)rdsz; } //FRESULT scan_files ( int scan_files ( //char* path /* Start node to be scanned (***also used as work area***) */ void /* Start node to be scanned (***also used as work area***) */ ) { FATFS fs; FRESULT res; DIR dir; UINT i; static FILINFO fno; int file_num=0; int file_name_pos; res = f_mount( &fs, "", 1 ); if(res == FR_OK) { res = f_opendir(&dir, "/"); /* Open the directory */ //return 0; if (res == FR_OK) { for (;;) { res = f_readdir(&dir, &fno); /* Read a directory item */ if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ if (fno.fattrib & AM_DIR) { /* It is a directory */ } else { /* It is a file. */ //sprintf(sbuff, "%s/%s\n", "/", fno.fname); sprintf(sbuff, "%d) %s\n", file_num,fno.fname); print(sbuff); file_name_pos=0; while(fno.fname[file_name_pos]!=0) { file_name[file_num][file_name_pos]=fno.fname[file_name_pos]; file_name_pos++; } file_name[file_num][file_name_pos]=fno.fname[file_name_pos]; file_num++; } } f_closedir(&dir); } } return file_num; } int move_to_sd_menu() { int file_num=0; print("\n"); print("TF menu> Select an action:\n"); print("\n"); print("TF menu> [1] print files in root dir\n"); print("TF menu> [2] cat file\n"); print("TF menu> [q] Exit TF card menu\n"); for (int rep = 10; rep > 0; rep--) { print("\n"); print("TF menu> Command> "); char cmd = getchar_wrap(); if (cmd > 32 && cmd < 127) putchar(cmd); print("\n"); switch (cmd) { case '1': file_num = scan_files(); break; case '2': if(file_num != 0) { print("input file no> "); while(1) { cmd = getchar_wrap(); if (cmd > 32 && cmd < 127) putchar(cmd); print("\n"); if((cmd>=48)&&(cmd<=57)) { // 0-9 sprintf(sbuff, "disp : %s\n\n", file_name[cmd-48]); print(sbuff); cat_file(file_name[cmd-48],buff_fattest, DEF_FATBUFF ); break; } else { sprintf(sbuff,"illegal no. range: %d - %d\n",0,file_num-1); print(sbuff); } } } else { print("first select no.1 or nothing files\n"); } break; case 'q': case 'Q': return 0; break; default: continue; } } } void main() { int fatfs_ret; int wsize; UART0->CLKDIV = CLK_FREQ / UART_BAUD - 2; GPIO0->OE = 0x3F; GPIO0->OUT = 0x3F; cmd_set_crm(1); cmd_set_dspi(1); print("\n"); print(" ____ _ ____ ____\n"); print(" | _ \\(_) ___ ___/ ___| ___ / ___|\n"); print(" | |_) | |/ __/ _ \\___ \\ / _ \\| |\n"); print(" | __/| | (_| (_) |__) | (_) | |___\n"); print(" |_| |_|\\___\\___/____/ \\___/ \\____|\n"); print("\n"); print(" On Lichee Tang Nano-9K\n"); print("This is modified firmware. add SD access.\n"); print("\n"); for ( i = 0 ; i < 10000; i++); GPIO0->OUT = 0x3F ^ 0x01; for ( i = 0 ; i < 10000; i++); GPIO0->OUT = 0x3F ^ 0x02; for ( i = 0 ; i < 10000; i++); GPIO0->OUT = 0x3F ^ 0x04; for ( i = 0 ; i < 10000; i++); GPIO0->OUT = 0x3F ^ 0x08; for ( i = 0 ; i < 10000; i++); GPIO0->OUT = 0x3F ^ 0x10; for ( i = 0 ; i < 10000; i++); GPIO0->OUT = 0x3F ^ 0x20; for ( i = 0 ; i < 10000; i++); GPIO0->OUT = 0x3F; for ( i = 0 ; i < 10000; i++); GPIO0->OUT = 0x00; for ( i = 0 ; i < 10000; i++); GPIO0->OUT = 0x3F; for ( i = 0 ; i < 10000; i++); // SD card fatfs_ret = fat_test_init(); if( fatfs_ret != 0 ) { print("fat_test_init() ERROR!\n" ); } while (1) { print("\n"); print("Select an action:\n"); print("\n"); print(" [1] Toggle led 1\n"); print(" [2] Toggle led 2\n"); print(" [3] Toggle led 3\n"); print(" [4] Toggle led 4\n"); print(" [5] Toggle led 5\n"); print(" [6] Toggle led 6\n"); print(" [F] Get flash mode\n"); print(" [I] Read SPI flash ID\n"); print(" [S] Set Single SPI mode\n"); print(" [D] Set DSPI mode\n"); print(" [C] Set DSPI+CRM mode\n"); print(" [B] Run simplistic benchmark\n"); print(" [A] Benchmark all configs\n"); print("<Additional Menu>\n"); print(" [Z] Check files in TF card\n"); for (int rep = 10; rep > 0; rep--) { print("\n"); print("IO State: "); print_hex(GPIO0->IN, 8); print("\n"); print("\n"); print("Command> "); char cmd = getchar_wrap(); if (cmd > 32 && cmd < 127) putchar(cmd); print("\n"); switch (cmd) { case 'F': case 'f': print("\n"); print("SPI State:\n"); print(" DSPI "); if ( cmd_get_dspi() ) print("ON\n"); else print("OFF\n"); print(" CRM "); if ( cmd_get_crm() ) print("ON\n"); else print("OFF\n"); break; case 'I': case 'i': cmd_read_flash_id(); break; case 'S': case 's': cmd_set_dspi(0); cmd_set_crm(0); break; case 'D': case 'd': cmd_set_crm(0); cmd_set_dspi(1); break; case 'C': case 'c': cmd_set_crm(1); cmd_set_dspi(1); break; case 'B': case 'b': cmd_benchmark(1, 0); break; case 'A': case 'a': cmd_benchmark_all(); break; case '1': GPIO0->OUT ^= 0x00000001; break; case '2': GPIO0->OUT ^= 0x00000002; break; case '3': GPIO0->OUT ^= 0x00000004; break; case '4': GPIO0->OUT ^= 0x00000008; break; case '5': GPIO0->OUT ^= 0x00000010; break; case '6': GPIO0->OUT ^= 0x00000020; break; case 'z': case 'Z': move_to_sd_menu(); break; default: continue; } } } } void irqCallback() { }
makeコマンドをたたくと、build/fw-flash.v が生成されます。
下記コマンドをDOS窓またはpower shellで実行しS1ボタンを押すとフラッシュへ書き込まれます。
python .\sw\pico-programmer.py .\fw\fw-flash\build\fw-flash.v COM14
メニューがHDMIに現れたら、teratermなどのシリアル端末で繋ぎます。Enterキーを押すとHDMIに表示されたメニューと同様なメニューがシリアル端末に表示されます
z→1 と押すとSDカードのrootに置いたファイル名が以下の様に表示されます。
0) test.txt 1) file1.txt 2) file2.txt
2を押すとinput file no> と聞かれます。
0-2のどれかを押すと、そのファイルの中身が表示されます。2を押すと
input file no> 2 disp : file2.txt this is file2
となり無事に中身が表示されます。
シリアルコンソールの表示の様子↓
_| ___ / ___| | |_) | |/ __/ _ \___ \ / _ \| | | __/| | (_| (_) |__) | (_) | |___ |_| |_|\___\___/____/ \___/ \____| On Lichee Tang Nano-9K This is modified firmware. add SD access. Select an action: [1] Toggle led 1 [2] Toggle led 2 [3] Toggle led 3 [4] Toggle led 4 [5] Toggle led 5 [6] Toggle led 6 [F] Get flash mode [I] Read SPI flash ID [S] Set Single SPI mode [D] Set DSPI mode [C] Set DSPI+CRM mode [B] Run simplistic benchmark [A] Benchmark all configs[Z] Check files in TF card IO State: 0000007f Command> IO State: 0000007f Command> z TF menu> Select an action: TF menu> [1] print files in root dir TF menu> [2] cat file TF menu> [q] Exit TF card menu TF menu> Command> 1 0) test.txt 1) file1.txt 2) file2.txt TF menu> Command> 2 input file no> 2 disp : file2.txt this is file2 TF menu> Command>
今回は以上となります。
SDカードへアクセスするために、ハードよりもソフト面が苦労しました。コンパイルオプションを変更しないとコンパイルが通らなかったりとか、stackサイズが足りずにハングアップとか。
次はボードに搭載されているSDRAMを動かしたいと思います。いつになることやら。。。
SDカードへのアクセスが遅いので、改善もさせたいです。
参考にさせていただいたサイト
PicoRV on nano 9K - Sipeed Wiki
TangNano-9K-example/picotiny at main · sipeed/TangNano-9K-example · GitHub
ラズパイPicoでFatFsを使う - Qiita
GitHub - nandland/spi-master: SPI Master for FPGA - VHDL and Verilog
Copyright (c) 2019 russell-merrick
MMC/SDCの使いかた
Release xPack GNU RISC-V Embedded GCC v8.2.0-3.1 · xpack-dev-tools/riscv-none-embed-gcc-xpack · GitHub
UG286-1.9.5J_Gowin Clockユーザーガイド.pdf
Tang_Nano_9K_3672_schematic.pdf
DS117-2.9.3E_GW1NR series of FPGA Products DataSheet.pdf
前回の記事はこちら
spend-carefree.hatenablog.com