Finally booting flash w/o connected terminal!

master
kaqu 2020-10-26 13:44:53 +01:00
parent 73f226cc66
commit 4015caedc8
15 changed files with 184 additions and 123 deletions

4
.vscode/launch.json vendored
View File

@ -10,8 +10,8 @@
"request": "launch",
"program": "${file}",
"args": ["--build",
"--load", // May be used separately ...
//"--flash", // May be used separately ...
//"--load", // May be used separately ...
"--flash", // May be used separately ...
"--revision=7.0",
"--uart-name=crossover",
//"--with-ethernet", // Not to be used together w/ etherbone! Won't TFTP ...

74
README.md Normal file
View File

@ -0,0 +1,74 @@
## Neopixelar - the FPGA project ##
This project demonstrates the use of LiteX & migen to create a Neopixel driving FPGA based h/w unit,
called NPE ('neopixel engine'). The project requires a colorlight-5a-75b board (sells at ~18€ currently). A RISC-V CPU (RV32I) is incorporated as well as network RAM loading (2 banks!) & separate application flashing capability (MUCH faster, bringing flashboot
to life!).
To use this project effectively, you will have to install LiteX, see https://github.com/enjoy-digital/litex for details.
Also, it is recommended to install the board support, see https://github.com/litex-hub/litex-boards.
To communicate with your board via network, install the wishbone tools, see https://github.com/litex-hub/wishbone-utils.
For board specific details see https://github.com/enjoy-digital/colorlite/blob/master.
Also, a JTAG programmer will be required for successful device programming. Thanx to Wolfgang, I'm using the Versaloon (s/w for blue-pill STM32), see https://github.com/zoobab/versaloon. To use this device, you also will have to install openocd:
apt install openocd
Other helpful links to board data:
https://github.com/q3k/chubby75/blob/master/5a-75b/hardware_V7.0.md
https://saturn.ffzg.hr/rot13/index.cgi?colorlight_5a_75b
https://github.com/trabucayre/litexOnColorlightLab004/
https://blog.pcbxprt.com/index.php/2020/07/19/running-risc-v-core-on-small-fpga-board/
To use the automatic documentation feature, you will have to install sphinx, see https://www.sphinx-doc.org/en/master.
Some helpful links for RST docstring formats:
http://daouzli.com/blog/docstring.html
https://thomas-cokelaer.info/tutorials/sphinx/rest_syntax.html
## Program structure: ##
*Neopixelar.py - this is the main FPGA building source
*neopixelengine.py - this is the actual NeoPixel driving engine
*helpers dir - contains python helpers for load & flash etc.
*firmware dir - contains some modified BIOS files (relative to the original version)
*software dir - contains a separate build, load & flash logic for separate (RV32i) application code
the rest is of minor importance ...
## QUICKSTART ##
After installation of the relevant toolchains:
1. Open the project in VSC (or use your favourite IDE & maybe adjust some settings ;), adjust local paths if nec. ...
2. Connect your JTAG adapter as described in Wolfgang's documentation @ https://git.hacknology.de/wolfgang/colorlight
3. Run neopixelar.py with these options (you may omit the --doc option if there is no Sphinx installed):
--build --load --revision=7.0 --uart-name=crossover --with-etherbone --ip-address=192.168.1.20 --csr-csv=build/csr.csv --doc
to create & load the project to on-board SRAM via the USB/JTAG-Adapter (this takes it's time ...)
4. Connect a Neopixel LED chain to J4/Pin 1 (the logic supports 16 pins w/ 256 LEDs per chain, the demo features 3 pins w/ 27 LEDs each)
5. Press reset on the board, the LED chain should become illuminated
6. To program this base logic permanently, the board has to be improved, add a capacitor to stabilize the voltage for FPGA
programming (see Wolfgang's documentation on https://git.hacknology.de/wolfgang/colorlight )
7. Now run the same sequence as mentioned above, this time replacing the --load option with the --flash option
8. This time it will take even longer ...
9. And hopefully complete without errors. If successful, the basic ROM s/w is now in place & operating. In case of errors try again - rule out EMI disturbances if nec.
10. Create the actual documentation for html via: 'sphinx-build -b html build/documentation build/documentation/html'
11. Use your favourite browser to access the 'npe' units documentation via 'file://<projectpath>/build/documentation build/documentation/html/index.html'
## Individual (separate) applications ##
1. This time, open up a terminal & cd to the project local software subdirectory
2. You can load an application to RAM bank 1:
./ramcreate.sh main illumination 1
3. To run the (now) RAM based application, type 'cd ..' within terminal
4. Connect the Litex-Terminal to the board via: wishbone-tool --ethernet-host 192.168.1.20 --server terminal --csr-csv build/csr.csv
5. Type 'ramboot' into terminal, the RAM based application should come up now
6. Press 's' (Speedup) to speed up the lights, 'w' (sloWdown) to slow them down
7. Press 'x' (eXit) to abort the application, you should see the LiteX prompt now
6. You can load an application to RAM bank 2:
./ramcreate.sh main illumination 2
7. Now, use 'ramboot' again! The system should swap to RAM bank #2 and boot the application right away
8. This is the testing loop, once your happy w/ your application, it needs to be flashed
9. !CAREFULL NOW! The application shouldn't contain any errors as it will be booted right after the BIOS
automatically
10. Make sure, your JTAG adapter is in place!
11. Run: ./ramcreate.sh main illumination 1
and then: ./flashcreate.sh main illumination
12. This flashes the application permanently to the boot address, where it will be verified by the BIOS
and started automatically (the good news: the 64kBytes of the sample application only take some 18 s ...)
Have fun!

8
firmware/README.md Normal file
View File

@ -0,0 +1,8 @@
This directory contains the modified versions of several BIOS related files.
The modifications are:
boot.c - doRAMBoot introduced
cmd_mem.c - dumpregs & ramboot introduced (mx, mhsig no longer used)
console.c - set_console, kbhit introduced, putchar modified
main.c - BIOS starter, modified for non-blocking operation

View File

@ -50,9 +50,9 @@ extern void boot_helper(unsigned long r1, unsigned long r2, unsigned long r3, un
static void __attribute__((noreturn)) boot(unsigned long r1, unsigned long r2, unsigned long r3, unsigned long addr)
{
printf("Executing booted program at 0x%08x\n\n", addr);
printf("--============= \e[1mLiftoff!\e[0m ===============--\n");
uart_sync();
//printf("Executing booted program at 0x%08x\n\n", addr);
//printf("--============= \e[1mLiftoff!\e[0m ===============--\n");
//uart_sync(); // This is a 'boot-locker'!
#ifdef CONFIG_CPU_HAS_INTERRUPT
irq_setmask(0);
irq_setie(0);
@ -455,17 +455,17 @@ static int copy_image_from_flash_to_ram(unsigned int flash_address, unsigned lon
if(length > 0) {
printf("Copying 0x%08x to 0x%08x (%d bytes)...\n", flash_address, ram_address, length);
offset = 0;
init_progression_bar(length);
//init_progression_bar(length);
while (length > 0) {
uint32_t chunk_length;
chunk_length = min(length, 0x8000); /* 32KB chunks */
memcpy((void *) ram_address + offset, (void*) flash_address + offset + 8, chunk_length);
offset += chunk_length;
length -= chunk_length;
show_progress(offset);
//show_progress(offset);
}
show_progress(offset);
printf("\n");
//show_progress(offset);
//printf("\n");
return 1;
}

View File

@ -595,7 +595,7 @@ static void ramboot(int nb_params, char **params)
// Executing in RAM bank #1?
if((cpc >= MAIN_RAM_BASE) && (cpc < (MAIN_RAM_BASE + (MAIN_RAM_SIZE / 2)))) {
ramno = 2; // Boot RAM[2] hence!
printf("\e[1mExecuting within RAM bank #1 -> Booting RAM banking #2 ...\e[0m\n");
//printf("\e[1mExecuting within RAM bank #1 -> Booting RAM banking #2 ...\e[0m\n");
c = (char *)(MAIN_RAM_BASE + (MAIN_RAM_SIZE / 2)); //0x40200000
}
else { // Nope, in RAM bank #2 assumed ...

View File

@ -26,10 +26,22 @@ void console_set_read_hook(console_read_hook r, console_read_nonblock_hook rn)
#define MAX_WO_RESET 80
static int output_count = 0;
static int console_non_blocking = 0;
static int uart_reset_count = 0;
void set_console(int non_blocking)
{
console_non_blocking = non_blocking;
if(non_blocking) {
busy_wait(100);
uart_init();
if((++uart_reset_count) > 9999)
uart_reset_count = 0;
}
}
int get_console()
{
return (console_non_blocking | (uart_reset_count << 24) | (output_count << 16));
}
#endif
@ -42,10 +54,12 @@ int putchar(int c)
putchar('\r');
#ifdef CONSOLE_NON_BLOCKING
if(console_non_blocking != 0) {
if(++output_count > MAX_WO_RESET) {
if((++output_count) > MAX_WO_RESET) {
extern void busy_wait(int);
busy_wait(100); // Make sure, output has been sent (115200/8 -> 14400Byte -> 55ms/80Byte)
uart_init(); // 'Unblock' ;-)
if((++uart_reset_count) > 9999)
uart_reset_count = 0;
output_count = 0;
}
}

View File

@ -186,7 +186,11 @@ int main(int i, char **c)
{
extern char kbhit(void);
extern void set_console(int non_blocking);
extern int get_console(void);
// Create our own start status indicator
// Read command: 'mr 0x403ffffc 4'
int32_t *statept = (int32_t *)(MAIN_RAM_BASE + MAIN_RAM_SIZE - sizeof(int32_t));
char buffer[CMD_LINE_BUFFER_SIZE];
char *params[MAX_PARAM];
char *command;
@ -204,14 +208,17 @@ int main(int i, char **c)
#define CONSOLE_NON_BLOCKING 1
#ifdef CONSOLE_NON_BLOCKING
busy_wait(1000); // Permit remote (IP) terminal to connect ...
printf("Press any key for full boot messages within 2s ...\n");
non_blocking = 1; //<-- Test w/ 0! 1; // By default: show demo (=1)...
printf("Press any key for full boot messages within 2s ...\n");
non_blocking = 1; //<-- Test w/ 0! 1; // By default: show demo (=1)...
for(int i=1;i<20;i++) {
busy_wait(100);
if(kbhit()) // Key pressed?
if(kbhit()) { // Key pressed?
non_blocking = 0; // Yap! Switch to blocking/full boot screen
}
break;
}
}
set_console(non_blocking); // 1=Non-blocking (0=blocking)
*statept = get_console(); // Make sure, we know what was going on ...
if(non_blocking) printf("\e[1mBoot screen skipped, going straight to illumination ...\e[0m\n");
#endif
@ -302,6 +309,7 @@ int main(int i, char **c)
#ifdef CONSOLE_NON_BLOCKING
if(!non_blocking)
set_console(1); // Allways non-blocking from now on ...
*statept = get_console(); // Make sure, we know what's going on ...
#endif
while(1) {
readline(buffer, CMD_LINE_BUFFER_SIZE);
@ -312,6 +320,7 @@ int main(int i, char **c)
if (!cmd)
printf("Command not found?!\n");
}
*statept = get_console(); // Make sure, we know what's going on ...
printf("\n%s", PROMPT);
}
return 0;

View File

@ -16,7 +16,6 @@
# 14.09.20/KQ Initial version, some output ports activated (user_led, J4) for testing
# 15.09.20/KQ Own logic exported to external module (neopixelprotocol.py - now neopixelengine.py)
# 19.09.20/KQ Project renamed 'NeoPixelar' (swedish plural for NeoPixel ...)
# 16.10.20/KQ UART w/o crossover now, requires serial terminal
#
# Build/Use ----------------------------------------------------------------------------------------
# - 'python3 neopixelar.py --build --revision=7.0 --uart-name=crossover --with-etherbone --ip-address=192.168.1.20 --csr-csv=build/csr.csv'
@ -27,7 +26,7 @@
# You should see the LiteX BIOS and be able to interact with it
# - To load a file to RAM (@0x40000000 len=0x400000) use:
# wishbone-tool --ethernet-host 192.168.1.20 --server load-file --csr-csv build/csr.csv
# --load-address 0x40100000
# --load-address 0x40000000
# --load-name build/colorlight_5a_75b/software/<filename>
# To disassemble raw file:
# ../fpga/litex/riscv64-unknown-elf-gcc-8.3.0-2019.08.0-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-objdump
@ -387,7 +386,7 @@ def main():
sdram_rate = args.sdram_rate,
**soc_core_argdict(args))
# 32MBit SPIFlash (not used currently) ---------------------------------------------------------------
# 32MBit SPIFlash ------------------------------------------------------------------------
flashbase = 0xc0000000
flashoffset = 0x100000 # Used to be zero (default)
soc.mem_map["spiflash"] = flashbase # Length: 0x01000000 ('til 0xc1000000 - 1)
@ -414,7 +413,7 @@ def main():
name = os.path.join(builder.gateware_dir, soc.build_name)
print(f"Executing ./bit_to_flash.py {name}.bit {name}.svf.flash")
from helpers.bit_to_flash import convertBitToFlashFile
convertBitToFlashFile(name + ".bit", name + ".svf.flash", address=flashoffset)
convertBitToFlashFile(name + ".bit", name + ".svf.flash", address=0)
from helpers.load_to_flash import load2flash
load2flash(name + ".svf.flash")
return

2
software/README.md Normal file
View File

@ -0,0 +1,2 @@
This directory contains the actual application & the loading/flashing tools.

View File

@ -9,7 +9,11 @@
# 23.10.20/KQ Worx great now!
#
echo "Preparing flash image ..."
./ramcreate.sh main illumination 1
echo "Flashing image ..."
cd ..
python3 helpers/imageflasher.py
cd software
cd software
echo "Done."

View File

@ -1,89 +0,0 @@
INCLUDE linker/output_format.ld
/* ENTRY(_start) */
ENTRY(illumination)
INCLUDE ../build/colorlight_5a_75b/software/include/generated/regions.ld
MEMORY {
spiflboot : ORIGIN = 0xc0100000, LENGTH = 0x00E00000
}
SECTIONS
{
.text :
{
_ftext = .;
/* Make sure crt0 files come first, and they, and the isr */
/* don't get disposed of by greedy optimisation */
*crt0*(.text)
KEEP(*crt0*(.text))
KEEP(*(.text.isr))
*(.text .stub .text.* .gnu.linkonce.t.*)
_etext = .;
} > spiflboot
.rodata :
{
. = ALIGN(8);
_frodata = .;
*(.rodata .rodata.* .gnu.linkonce.r.*)
*(.rodata1)
*(.got .got.*)
*(.toc .toc.*)
/* Make sure the file is aligned on disk as well
as in memory; CRC calculation requires that. */
FILL(0);
. = ALIGN(8);
_erodata = .;
} > spiflboot
.commands :
{
PROVIDE_HIDDEN (__bios_cmd_start = .);
KEEP(*(.bios_cmd))
PROVIDE_HIDDEN (__bios_cmd_end = .);
} > spiflboot
.data :
{
. = ALIGN(8);
_fdata = .;
*(.data .data.* .gnu.linkonce.d.*)
*(.data1)
*(.sdata .sdata.* .gnu.linkonce.s.*)
/* Make sure the file is aligned on disk as well
as in memory; CRC calculation requires that. */
FILL(0);
. = ALIGN(8);
_edata = .;
} > sram AT > spiflboot
.bss :
{
. = ALIGN(8);
_fbss = .;
*(.dynsbss)
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(8);
_ebss = .;
_end = .;
} > sram AT > spiflboot
/DISCARD/ :
{
*(.eh_frame)
*(.comment)
}
}
PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 8);
PROVIDE(_fdata_rom = LOADADDR(.data));
PROVIDE(_edata_rom = LOADADDR(.data) + SIZEOF(.data));

View File

@ -78,6 +78,8 @@ fi
echo "--"
echo "Creating flashable image"
python3 -m litex.soc.software.mkmscimg build/$1.bin --little --fbi --output build/$1.img
#<<<--- TEST ONLY!
#cp build/$1.bin build/$1.img
echo "--"
echo "loading build/$1.bin to RAM base via wishbone-tool"
if [ "$3" = "1" ]; then

View File

@ -0,0 +1,5 @@
main.c - This is the rudimentary BIOS loop
illumination.c - This is a sample application, demonstrating the use
of the 'neopixelengine' (the documentation can be found
under ./build/documentation/http/index.html)

View File

@ -128,7 +128,10 @@ int illumination(void)
load_triple_LEDs(iTable, green, red, blue);
send_LEDs();
busy_wait(iDelay); // Slow down a bit
if(key_eval()) return 1;
if(key_eval()) {
enable_LEDS(0);
return 1;
}
}
// Make 3 run along ...
green = 0x404000;
@ -149,7 +152,10 @@ int illumination(void)
arLEDBuffer[iTable][max_LED - j] = arLEDBuffer[iTable][(max_LED - 1) - j];
arLEDBuffer[iTable][i] = 0;
}
if(key_eval()) return 1;
if(key_eval()) {
enable_LEDS(0);
return 1;
}
}
printf("Halfway thru ...\n");
for(int i=0;i<MAXLEDS-1;i++) { // Backward shift 3
@ -160,7 +166,10 @@ int illumination(void)
arLEDBuffer[iTable][j] = arLEDBuffer[iTable][j+1];
arLEDBuffer[iTable][max_LED-i] = 0;
}
if(key_eval()) return 1;
if(key_eval()) {
enable_LEDS(0);
return 1;
}
}
busy_wait(400);

View File

@ -47,13 +47,16 @@ extern char kbhit(void);
#define CONSOLE_NON_BLOCKING 1
#ifdef CONSOLE_NON_BLOCKING
extern void set_console(int non_blocking);
extern int get_console(void);
#endif
extern int readchar_nonblock(void);
extern int32_t cpc; // Current PC storage
static int32_t mycpc; // Current PC storage
int main(int i, char **c)
{
// Create our own start status indicator
// Read command: 'mr 0x403ffffc 4'
int32_t *statept = (int32_t *)(MAIN_RAM_BASE + MAIN_RAM_SIZE - sizeof(int32_t));
char buffer[CMD_LINE_BUFFER_SIZE];
char *params[MAX_PARAM];
char *command;
@ -64,26 +67,44 @@ int main(int i, char **c)
irq_setmask(0);
irq_setie(1);
#endif
uart_init();
// Prepare first output
int32_t green = 0x040201;
int32_t red = 0x010402;
int32_t blue = 0x020104;
int iTable;
iTable = 0;
load_triple_LEDs(iTable, green, red, blue); // 1st load
iTable = 1;
load_triple_LEDs(iTable, red, blue, green);
iTable = 2;
load_triple_LEDs(iTable, blue, green, red);
send_LEDs();
enable_LEDS(1);
busy_wait(2000);
enable_LEDS(0);
uart_init();
printf("\n");
// Verify we're executing within RAM ...
__asm__ __volatile__ ("\
lui t0,%hi(cpc) # Load t0 w/ high adress of cpc \n\
addi t0,t0,%lo(cpc) # Add immediate t0=t0 + low adress cpc \n\
lui t0,%hi(mycpc) # Load t0 w/ high adress of cpc \n\
addi t0,t0,%lo(mycpc) # Add immediate t0=t0 + low adress cpc \n\
# t0 now loaded with pointer to cpc \n\
auipc t1,0 # Load current pc \n\
sw t1,0(t0) # Store current PC \n\
");
if((cpc >= MAIN_RAM_BASE) && (cpc <= (MAIN_RAM_BASE + MAIN_RAM_SIZE))) {
printf("\e[1mExecuting within RAM ...\e[0m\n");
if((mycpc >= MAIN_RAM_BASE) && (mycpc < (MAIN_RAM_BASE + MAIN_RAM_SIZE))) {
#ifdef CONSOLE_NON_BLOCKING
set_console(1); // 1=Non-blocking (0=standard/blocking)
*statept = get_console(); // Make sure, we know what was going on ...
#endif
printf("\e[1mExecuting within RAM ...\e[0m\n");
while(1)
if(illumination()) // The DEMO !
break;
*statept = get_console(); // Make sure, we know what was going on ...
while(1) {
printf("\n%s", PROMPT);
readline(buffer, CMD_LINE_BUFFER_SIZE);
@ -96,10 +117,13 @@ int main(int i, char **c)
illumination(); // more DEMO ...
}
}
#ifdef CONSOLE_NON_BLOCKING
*statept = get_console(); // Make sure, we know what was going on ...
#endif
}
}
else
printf("Non-RAM execution @%08Xh, no action ...\n", cpc);
//else
// printf("Non-RAM execution @%08Xh, no action ...\n", cpc);
while(1);
return 0;
}