This example application demonstrates one of the use-cases of the eFPGA core in the EOSS3. The M4 Subsystem in the EOSS3 has access to a GPIO Controller, which has support for only 8 pads to be used as a GPIO. If those pads are already muxed to different functions, it becomes restrictive in the number of GPIOs available in the system.
The eFPGA on the EOSS3 can access all of the 46 pads (IO_0 to IO_45) and use it according to any design on the eFPGA.
In this application, we take the first 32 pads (IO_0 to IO_31) and enable usage as GPIO pins via the eFPGA design.
To control the GPIOs exposed in this way, from the M4, we use the AHB-Wishbone Interconnect between the M4 Subsystem and the eFPGA, hence making it possible to use it like any other peripheral on the M4 Subsystem.
Using a set of 32-bit registers, exposed over the AHB-Wishbone Interconnect, M4 code can just set bits on the registers and control the IO pads as either input (to read the logic value) or as output (to set the logic value).
note: ensure that you have connected a USB-UART adapter connected to the EOSS3 UART pins (IO_44/IO_45), and you have a serial terminal connected to that port at 115200 8N1.
Once the code is running, you should see a pattern of 3 Magenta Blinks (after the flashing BLUE LED pattern put out by the bootloader as usual) indicating the code has loaded and started execution (FPGA and M4)
This pattern of 3 Magenta Blinks is executed using the eFPGA based GPIO Controller Design itself.
This can be see in the code of src/main.c [Line 85 - Line 102]
This is followed by a banner like the below on the serial terminal:
##########################
OnionApps FPGA GPIO Controller Example
SW Version: qorc-onion-apps/qorc_fpga_gpioctrl
Feb 19 2021 18:57:15
##########################
Hello GPIO CONTROLLER!
#*******************
Command Line Interface
App SW Version: qorc-onion-apps/qorc_fpga_gpioctrl
#*******************
FPGA Device ID: 0x0000c001
[0] >
Enter the GPIO Controller submenu using gpioctrl
and then type help
in the gpioctrl submenu for commands.
[0] > gpioctrl
[1] gpioctrl > help
help-path: gpioctrl
setout - setout IO_X VAL
setin - setin IO_X
getval - getval IO_X (must be setin first)
exit - exit/leave menu
help - show help
? - show help
help-end:
To set a GPIO as output with specific value, use setout IO_PADNUMBER VALUE_TO_SET
For example, to set IO_22 to value 1 (this is connected to RED LED on the PygmyBB4/QF):
[1] gpioctrl > setout 22 1
io = 22
val = 1
The RED LED should have turned on.
To set IO_22 to value 0:
[1] gpioctrl > setout 22 0
io = 22
val = 0
The RED LED should have turned off.
To read GPIO value, we need to ensure that this IO has been set as input first using setin IO_PADNUMBER
and
then we can read the value using getval IO_PADNUMBER
For reading IO_5 for example, we set it to input mode first:
[1] gpioctrl > setin 5
io = 5
Connect a Jumper Wire from IO_5 to 3V3 on the PygmyBB4/QF board, and the read the value:
[1] gpioctrl > getval 5
io = 5
val = 0
read value = 1
Now connect the Jumper Wire from IO_5 to GND instead, and read the value:
[1] gpioctrl > getval 5
io = 5
val = 1
read value = 0
Note that the IO_PADNUMBER
is the actual pad number of the EOSS3 and is clearly marked on the PygmyBB4 pins.
In the schematic also, we can see this pad number mentioned, for example, IO_22 can be seen connected to R_LED.
In brief, the GPIO Controller is instantiated in the FPGA, and defines 3 registers to control IO_0 - IO_31.
These registers are accessed like normal 32-bit registers from M4 code using the AHB-Wishbone interconnect.
From the M4 side, it looks like memory mapped AHB registers, just like any other peripheral.
The AHB-Wishbone Bridge converts the AHB read/write transactions into Wishbone read/write transactions.
We implement the Wishbone read/write transaction decoding in the eFPGA verilog code, and interpret the register read/write into logic for GPIO Control.
The OFFSETS of these registers are: [fpga/rtl/GPIO_controller.v]
localparam REG_ADDR_GPIO_IN = 8'h00 ;
localparam REG_ADDR_GPIO_OUT = 8'h04 ;
localparam REG_ADDR_GPIO_OE = 8'h08 ;
The GPIO Controller itself is defined to have an OFFSET as below: [fpga/rtl/AL4S3B_FPGA_IP.v]
parameter GPIO_BASE_ADDRESS = 17'h04000 ;
Finally, the FPGA BASE ADDRESS (when accessed from the AHB side of M4) is 0x40020000
[HAL/inc/eoss3_dev.h]
Using this information, we see that the basic design is :
- Control GPIO as output or input by setting 1 or 0 respectively to the appropriate bit in
REG_ADDR_GPIO_OE (0x40024008)
- If set as output, set the appropriate bit as 1 or 0 for High/Low in
REG_ADDR_GPIO_OUT (0x40024004)
- If set as input read the value at appropriate bit from
REG_ADDR_GPIO_IN (0x40024000)
Note: all the commands below are run from the root of this directory.
Before clean/build/load/flash, ensure that the bash environment is setup by doing the below:
Ensure that QORC-SDK is initialized and ready:
source ../../envsetup.sh
note: assumes this repo 'qorc-onion-apps' is cloned into the QORC SDK dir, at the same level as qf_apps
In general, use:
source <QORC_SDK_PATH>/envsetup.sh
[Only] If you are using the RP2040/Pico as a debugger (picoprobe), then ensure that the RaspberryPi flavor of OpenOCD is initialized and ready:
source .scaffolding/onion_openocd_picoprobe_setup.sh
If you want to use the SVD file for EOS-S3 to watch registers while debugging, then ensure that the SVD file is initialized and ready:
source .scaffolding/onion_svd_setup.sh
note: the SVD file is in very early alpha and missing many registers, use with discretion!
To force download of latest SVD file from repo and replace the existing one, use:
source .scaffolding/onion_svd_setup.sh force
Clean using:
fpga:
make clean-fpga
m4:
make clean-m4
both:
make clean
Build using:
fpga:
make fpga
m4:
make m4
both:
make
Load and run the code/design on the board using JLinkExe, using:
(assumes the board has been booted in DEBUG mode)
make load-jlink
Load and run the code/design on the board using OpenOCD, using:
(assumes the board has been booted in DEBUG mode)
export QORC_OCD_IF_CFG=/path/to/inteface/cfg # needs to be done only once in the current shell make load-openocd
The interface cfg file depends on the debug adapter chosen.
Here are a few common adapters that can be used with the EOS_S3:
- JLink Adapters:
export QORC_OCD_IF_CFG=.scaffolding/jlink_swd.cfg
(available in the current dir) - FT2232H Boards:
export QORC_OCD_IF_CFG=.scaffolding/ft2232h_swd.cfg
(available in the current dir) - STLinkv2 Adapters:
export QORC_OCD_IF_CFG=interface/stlink-v2.cfg
(available in the OpenOCD install scripts dir) - DAPLink Adapters:
export QORC_OCD_IF_CFG=interface/cmsis-dap.cfg
(available in the OpenOCD install scripts dir) - picoprobe:
export QORC_OCD_IF_CFG=interface/picoprobe.cfg
(available in the OpenOCD install scripts dir, only if RaspberryPi flavor OpenOCD has been initialized)
Practically, any adapter that supports OpenOCD and SWD can be used with the appropriate cfg file passed in.
- JLink Adapters:
Flash and run the code/design on the board using qfprog:
(assumes the board is put into
programming
mode)export QORC_PORT=/path/to/serial/port # needs to be done only once in current shell make flash
Set the serial port as applicable, this is generally
export QORC_PORT=/dev/ttyACM0
- VS Code Extension:
ms-vscode.cpptools
why: C/C++ Intellisense, Debugging - VS Code Extension:
marus25.cortex-debug
why: Cortex-M Debug Launch Configuration - VS Code Extension:
augustocdias.tasks-shell-input
why: Scan serial-ports forflash
task, Select FPGA '.openocd' file forDebug (OpenOCD)
debug launch config
The first time the project is going to be used from VS Code, we need to do the following:
copy
.vscode/settings.template.jsonc
as.vscode/settings.json
Ensure the following variables are correctly defined:
"qorc_sdk_path" : "${workspaceFolder}/../..",
In VS Code:
${env:HOME}
refers to $HOME of the current user${workspaceFolder}
refers to the current directoryRemaining variables don't need to be changed.
Open the current directory in VS Code using
File > Open Folder
menu- To be able to run the 'flash' task or 'Debug (OpenOCD)' launch config, remember to install the extension:
augustocdias.tasks-shell-input
- To be able to 'debug' the code with gdb, remember to install the extension:
marus25.cortex-debug
On opening the folder, VS Code should prompt to install these "recommended extensions", if not already installed, select
Install All
to automatically install them.- To be able to run the 'flash' task or 'Debug (OpenOCD)' launch config, remember to install the extension:
Any "task" can be run in VS Code using the Terminal > Run Task
menu, which shows a drop down list of tasks
-OR-
Using keyboard shortcuts: ctrl+p
and then type task<space>
, which shows a drop down list of tasks
Clean using:
- fpga: run the
clean-fpga
task - m4: run the
clean-m4
task - both: run the
clean
task
- fpga: run the
Build using:
- fpga: run the
build-fpga
task - m4: run the
build-m4
task - both: run the
build
task
- fpga: run the
Load and run the code/design on the board using JLinkExe, using:
(assumes the board has been booted in DEBUG mode)
run the
load (JLink)
taskLoad and run the code/design on the board using OpenOCD, using:
(assumes the board has been booted in DEBUG mode)
run the
load (OpenOCD)
taskThis will show a drop down menu with the options of debug adapters currently tested:
- JLink Adapters
.scaffolding/jlink_swd.cfg
- FT2232H Boards
.scaffolding/ft2232h_swd.cfg
- STLinkv2 Adapters
interface/stlink-v2.cfg
- DAPLink Adapters
interface/cmsis-dap.cfg
select the appropriate one.
- JLink Adapters
Load and run the code/design on the board using OpenOCD and picoprobe, using:
(assumes the board has been booted in DEBUG mode)
run the
load (OpenOCD-picoprobe)
taskFlash and run the code/design on the board using qfprog:
(assumes the board is put into
programming
mode)run the
flash
taskThis will show a 'pickstring' drop down menu with the available serial ports in the system, select the appropriate one.
(This is usually
/dev/ttyACM0
)load-fpga-debug (JLink)
: This is a special task required only while debugging the code with JLink.Refer to the Debug sections for details.
x-get-ports
: this is an internal task, which is used by theflash
task to obtain a list of available serial ports on the system to use for flashing. This list is displayed to the user as a 'pickstring' dropdown menu, as described in theflash
task above.
Debug the code via JLink :
- To bring up the
Run and Debug
view, select the Run icon in the Activity Bar on the side of VS Code. - Select
Debug (JLink)
from the drop down at the top of the side bar - Start Debugging by clicking the green
Play Button
- The code should load and break at
main()
- Run the
load-fpga-debug (JLink)
task at this point, to load the FPGA design - Resume/Continue debugging using the blue
Continue/Break
button at the top or usingF8
- To bring up the
Debug the code via OpenOCD :
To bring up the
Run and Debug
view, select the Run icon in the Activity Bar on the side of VS Code.Select
Debug (OpenOCD)
from the drop down at the top of the side barStart Debugging by clicking the green
Play Button
A drop-down menu appears to select the debug adapter being used, currently the choices are:
.scaffolding/jlink_swd.cfg
.scaffolding/ft2232h_swd.cfg
interface/stlink-v2.cfg
interface/cmsis-dap.cfg
More can be added in the
launch.json
file.Select the appropriate one.
The fpga bitstream (.openocd) should get loaded, then the m4 code should load and break at
main()
Resume/Continue debugging using the blue
Continue/Break
button at the top or usingF8
Debug the code via OpenOCD and picoprobe :
- To bring up the
Run and Debug
view, select the Run icon in the Activity Bar on the side of VS Code. - Select
Debug (OpenOCD-picoprobe)
from the drop down at the top of the side bar - Start Debugging by clicking the green
Play Button
- The fpga bitstream (.openocd) should get loaded, then the m4 code should load and break at
main()
- Resume/Continue debugging using the blue
Continue/Break
button at the top or usingF8
- To bring up the
Common Debugging Steps with the
Cortex-Debug
extension in VS Code:- Place breakpoints in the code by clicking near the line number
- Use the
Step Over
,Step Into
,Step Out
,Restart
,Stop
buttons to control the debugging session