Vivado/XSDK: How to access address from Zynq M_AXI_GP0 Bus? - c

Let's say I built a vivado Zynq FPGA project, and I want to write and read the Zynq's "M_AXI_GP0" port from a c-program running on the zynq as follows. Further, let's suppose the address I want to read and write on the "M_AXI_GP0" port of the Zynq is address "0x000A1000". How would I do that in the code below? Do I need to worry about virtual address to physical address translation in the ARM? is there a XIL api for that? etc... see code example:
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
void write(uint32_t addr, uint32_t wdata) {
//????? how to implement
}
uint32_t read(uint32_t addr) {
return 0;
}
int main()
{
init_platform();
print("Hello World\n\r");
uint32_t beef;
write(0x000A1000, 0xDEADBEEF);
write(0x000A1004, 0x12345678);
beef = read(0x000A1000);
cleanup_platform();
return 0;
}

The "xil_ io.h" file contains the interface for the general IO component, which encapsulates the Input/Output functions for processors that do not require any special I/O handling:
u32 Xil_In32 (UINTPTR Addr);
void Xil_Out32 (UINTPTR Addr, u32 Value);
The "M_AXI_GP0" Bus is mapped to the PL or "Programmable Logic" address region in the Zynq Address Map:
Xilinx Zynq: ARM Cortex A9 Memory Map
DDR 0x00000000 - 0x3FFFFFFF
PL 0x40000000 - 0xBFFFFFFF
Reserved 0xC0000000 - 0xDFFFFFFF
Memory mapped devices 0xE0000000 - 0xE02FFFFF
Reserved 0xE0300000 - 0xE0FFFFFF
NAND, NOR 0xE1000000 - 0xE3FFFFFF
SRAM 0xE4000000 - 0xE5FFFFFF
Reserved 0xE6000000 - 0xF7FFFFFF
AMBA APB Peripherals 0xF8000000 - 0xF8FFFFFF
Reserved 0xF9000000 - 0xFBFFFFFF
Linear QSPI - XIP 0xFC000000 - 0xFDFFFFFF
Reserved 0xFE000000 - 0xFFEFFFFF
OCM 0xFFF00000 - 0xFFFFFFFF

Did you see the AXI GPIO driver and example in here? Whether you need to worry about virtual to physical address translation depends on what exactly are you running? Do you have a bare metal/RTOS setup?

Related

Issues creating a Linux device driver to manage NanoPI TWI (I2C) registers

I'm trying to develop a Linux device driver to manage the GPIO registers of a NanoPI Neo card (Allwinner H3).
I'm using a simple approach to understand the GPIO registers behaviour by means the use of only two driver functions: open and ioctl.
My driver implementation, at now, is able to manage a lot of registers such as RTC and CPU-PORT and to read write some other registers.
But I'm having issues in using/managing TWI registers (I2C).
The blocking me issue is that whathever register I read or read after writing any value to the register itself always returns 0x00000000 and nothing seems to happen at pin HW level (PA11/PA12 see below)
I read on the CPU datasheet (see: paragraph 8.1 of the document) the registers value and base address to manage TWI0, base address should be 0x01C2AC00.
I can't find any AllWinner H3 GPIO programmer's reference and I'm not sure if specific operations are required to activate the TWI register functionality. The only operations I have done are to set registers PA11 and PA12 to be the I/O of TWI0_SCK and TWI0_SDA.
Questions:
Do you have any news of an "AllWinner H3 GPIO Programmer's Reference"?
Do you know which GPIO register I have to set / modify to get TWI enabled or at least give me "signs of life"?
A very strong simplification, aimed at using only TWI0 registers, of the mapping and ioctl functions of the driver I wrote might be the following code, but my code is much more sophisticated and I know it works with many other registers.
#define TWI_IOBASE(n) (0x01C2AC00 + 0x400*((n)&3))
#define TWI_PAGESIZE 0x400
#define IO_ADDRESS_MSK 0x0000FFFFUL
#define IO_CMD_MSK 0xF0000000UL
#define IO_CMD_READ 0x10000000UL
#define IO_CMD_WRITE 0x20000000UL
static unsigned char * vmaddr;
inline static int drv_init_twi_vm(void)
{
vmaddr=ioremap(TWI_IOBASE(0), TWI_PAGESIZE);
}
static long drv_ioctl_twi_rw(struct file *file, unsigned int cmd, unsigned long * arg)
{
long retval = 1;
unsigned c;
uint32_t r;
void *x;
r=cmd & IO_ADDRESS_MSK;
x=vmaddr+r;
c=cmd & IO_CMD_MSK;
switch(c)
{
case IO_CMD_READ:
if ( copy_to_user((void *)arg, x, 4) ) {
retval = -EFAULT;
}
break;
case IO_CMD_WRITE:
*(unsigned long *)x=*arg;
break;
default:
retval=-EFAULT;
break;
}
return retval;
}

Do Struct-style register definitions consume RAM

So I've been scouring the internet for an answer and have not gotten anywhere.
What I want to know is if struct-style register definitions for microcontrollers (ARM mcu, AVR mcu) consume RAM. I know that if an object of a struct is instantiated it will consume RAM (either on the stack or otherwise).
But what about register definitions like the ones below, that are used by ARM in their CMSIS and similar to what the new ATTiny series use for their register definitions as far as I know.
Do these consume RAM memory specifically. I'm quite sure they would somewhat consume flash/program space, but RAM?
#define PORT ((Port *)0x41008000UL) /**< \brief (PORT) APB Base Address */
typedef struct {
PortGroup Group[4]; /**< \brief Offset: 0x00 PortGroup groups [GROUPS] */
} Port;
typedef struct {
__IO PORT_DIR_Type DIR; /**< \brief Offset: 0x00 (R/W 32) Data Direction */
__IO PORT_DIRCLR_Type DIRCLR; /**< \brief Offset: 0x04 (R/W 32) Data Direction Clear */
__IO PORT_DIRSET_Type DIRSET; /**< \brief Offset: 0x08 (R/W 32) Data Direction Set */
__IO PORT_DIRTGL_Type DIRTGL; /**< \brief Offset: 0x0C (R/W 32) Data Direction Toggle */
__IO PORT_OUT_Type OUT; /**< \brief Offset: 0x10 (R/W 32) Data Output Value */
__IO PORT_OUTCLR_Type OUTCLR; /**< \brief Offset: 0x14 (R/W 32) Data Output Value Clear */
__IO PORT_OUTSET_Type OUTSET; /**< \brief Offset: 0x18 (R/W 32) Data Output Value Set */
__IO PORT_OUTTGL_Type OUTTGL; /**< \brief Offset: 0x1C (R/W 32) Data Output Value Toggle */
__I PORT_IN_Type IN; /**< \brief Offset: 0x20 (R/ 32) Data Input Value */
__IO PORT_CTRL_Type CTRL; /**< \brief Offset: 0x24 (R/W 32) Control */
__O PORT_WRCONFIG_Type WRCONFIG; /**< \brief Offset: 0x28 ( /W 32) Write Configuration */
__IO PORT_EVCTRL_Type EVCTRL; /**< \brief Offset: 0x2C (R/W 32) Event Input Control */
__IO PORT_PMUX_Type PMUX[16]; /**< \brief Offset: 0x30 (R/W 8) Peripheral Multiplexing */
__IO PORT_PINCFG_Type PINCFG[32]; /**< \brief Offset: 0x40 (R/W 8) Pin Configuration */
RoReg8 Reserved1[0x20];
} PortGroup;
NOTE : All the code provided in the code blocks above are direct quotes from a user on EEVBlog. It is regarding register definitions provided in CMSIS. Link is here.
EDIT : I understand that MCUs have registers and accessing those registers don't consume RAM. But my confusion is in the way these registers are being referred to. For example :
// if a register address is 0x50
#define address 0x50 // This consumes no RAM as it is resolved during compilation
uint8_t addr = 0x50; // This consumes RAM because it is now a variable
// So what about this??
typedef struct {
uint8_t addr = 0x50;
} address_group;
So accessing the registers themselves won't use up RAM space, but the method we use to easily refer to those addresses, in this case using structs, is what I'm confused about.
All registers can be #define'd one by one which will consume no RAM, but the choice of doing it in struct format...?
In order to control peripherals such as GPIO and UART, the peripherals provide registers that are mapped into the memory address space of the MCU.
The typical address range for such registers on STM32 MCUS is from 0x40000000 upwards. This is separate from both flash and RAM.
As an example: The baud rate register for USART1 might be at address 0x41006008. So by reading a word (32 bit) from that address, the baud rate can be read. And by writing to the address, it can be changed.
In C, it could look like:
*(volatile uint32_t*)0x41006008 = 115200;
However, it's much more readable and efficient if it looks like so:
USART1.BRR = 115200;
All the defines and typedefs shown in your question declare data types (such as PortGroup) and pseudo variables (such as PORT). I call them pseudo because they are not regular variables allocated in RAM but rather memory mapped structures built into hardware. (And data type declaration never consume memory anyway.)
The neat thing about this approach is: the code is easier to write and read and still the same size as the cryptic code. It's resolved at compile time as the compiler can figure out the absolute address of USART1.BRR.
Strictly speaking, memory-mapped registers are RAM, in a way, though they can be updated by hardware and software both. They are located in the memory map at a location where the linker isn't allowed to mess around. The compiler in general has no awareness that the register area exists.
By using methods like those described here: How to access a hardware register from firmware? , or by using a pre-made "register map" from the vendor, we tell the compiler to access an area of memory where it has no clue what's stored.
You can tell the compiler for example "there's a PortGroup struct at this location, from address 0x41008000UL and beyond". The compiler then blindly trusts the programmer and accesses that area through the pointer provided, using the declared types of the struct. If these types matches the hardware registers, then everything will work just as if you had allocated variables there. But you actually don't need to have make the linker allocate memory in that area, because everything is already provided in the hardware.
Accessing the registers, or accessing any other form of variable for that matter, doesn't necessarily take any RAM. Given any random variable int x;, then x itself is allocated somewhere, but the act of accessing it with x=0; etc doesn't likely take any RAM.
And finally some misconceptions: #define address 0x50 doesn't consume any RAM, not because it is resolved at compile-time, but because it consumes ROM instead. All numbers used by the program will be allocated somewhere in the executable.
If you have uint8_t addr = 0x50; ... if(addr == something), the optimizing compiler does not necessarily have to allocate RAM space for addr. It could optimize away the whole variable. See this example for x86: https://godbolt.org/z/KroKhq. It didn't make any sense to allocate the variable so the compiler stored the magic number 0x50 (80 dec) in the machine code, as part of the cmp instruction. You will get identical machine code if you use #define in the same example.
No, those structures don't take up RAM. RAM is consumeed when you actually define a variable, with code like this
unsigned int myInteger; // takes RAM
struct foo myStruct; // takes RAM
Lines like the ones above tell your compiler/linker to make symbols called myInteger and myStruct and allocate space for those symbols in the RAM area of your microcontroller (possibly on your stack).
On the other hand, when you define a struct type or make a typedef, that is different from defining a variable, and it doesn't take up any RAM:
struct foo { // Does not take RAM, just defines a type.
...
};
When you define a pointer to a struct using the code below, you are just defining an expression that evaluates to a pointer to a struct that already exists in the hardware. You aren't doing anything that should consume RAM:
// Does not take up RAM, just defines an expression.
#define PORTA ((struct GPIO *)0x1230)
When you use PORTA in your code, the lines of code that use it will take some code space. Those lines might take some stack space when they execute, but they should not take up any RAM permanently.

Enable GPIO on AM335x in C

I have the following function initGPIO. The goal is to enable the GPIOs 0, 1 and 2 on beaglebone using am335x. How can I enable the corresponding GPIO set to the reg_GPIO that are given in the header file? I have the header file GPIO.h which contains the GPIO's numbers, register number and register structure. I have tried to set the GPIO in the function initGPIO. Does it make sense?
gpio.h
#include <stdint.h>
#include <stdbool.h>
#define GPIO0 0 /*!< GPIO 0 number */
#define GPIO1 1 /*!< GPIO 1 number */
#define GPIO2 2 /*!< GPIO 2 number */
#define GPIO3 3 /*!< GPIO 3 number */
// Base address for each gpio hardware module.
#define GPIO0_REG 0x44E07000 //<! gpio0 hardware module address.
#define GPIO1_REG 0x4804C000 //<! gpio1 hardware module address.
#define GPIO2_REG 0x481AC000 //<! gpio2 hardware module address.
#define GPIO3_REG 0x481AE000 //<! gpio3 hardware module address.
// Register Structure
typedef struct
{
volatile uint32_t irqstatus_set_0; // Offset 0x34 - Enables specific interrupt event to trigger.
volatile uint32_t irqstatus_set_1; // Offset 0x38 - Enables specific interrupt event to trigger.
volatile uint32_t irqwaken_0; // Offset 0x44 - Enables wakeup events on an interrupt.
volatile uint32_t irqwaken_1; // Offset 0x48 - Enables wakeup events on an interrupt.
volatile uint32_t ctrl; // Offset 0x130 - Controls clock gating functionality, i.e. enables module.
volatile uint32_t oe; // Offset 0x134 – Output Enable pin (clear bit to 0) output capability.
volatile uint32_t datain; // Offset 0x138 - Registers data read from the GPIO pins.
volatile uint32_t dataout; // Offset 0x13c - Sets value of GPIO output pins.
volatile uint32_t cleardataout; // Offset 0x190 - Clears to 0 bits in dataout
volatile uint32_t setdataout; // Offset 0x194 - Sets to 1 bits in dataout
} GPIO_REGS;
void initGPIO();
gpio.c
/*!
* \brief Initialize GPIOs.
*
* Enables GPIO0, GPIO1, and GPIO2 (GPIO3 not used in IDP. Also configures the output pins
* used in the IDP to control relays and address the ADC's on relays.
*
*****************************************************************************************/
void initGPIO()
{
//enable GPIOs
GPIO_REGS gpio_regs;
//might need to change ctrl
gpio_regs.datain |= (GPIO0 << GPIO0_REG );
gpio_regs.datain |= (GPIO1 << GPIO1_REG );
gpio_regs.datain |= (GPIO2 << GPIO2_REG );
}
By default Beaglebones come with Debian Linux. Unless you've decided to drop that, the kernel has a GPIO driver which assumes control of all GPIO. You should not try to access raw GPIO registers directly, but rather talk to the kernel driver. The simplest way to do that is to install libgpiod (which may be installed by default on recent Beagles) and call its API.
For bare bone platforms you may be used to do something like this to access hardware addresses:
volatile GPIO_REGS* gpio1_regs = (GPIO_REGS*)0x44E07000;
volatile GPIO_REGS* gpio2_regs = (GPIO_REGS*)0x4804C000;
Now your code isn't doing this either, but I'm going to assume that is what you meant.
Except inside an OS the memory offsets in you user-space application don't map one-on-one to hardware address offsets and this simply won't work.
If you want direct register access to things like this, you'll have to do something like Memory Mapping using /dev/mem or if your platform supports it, /dev/gpiomem (I know Raspberry Pi supports this, I'm not sure about the BBB). This will check if you are allowed to access the hardware address range you want, as well as make sure the address range is mapped correctly in your user-space.
For example:
/* open /dev/mem */
int fd = open("/dev/mem", O_RDWR|O_SYNC);
if (fd >= 0) {
/* mmap GPIO */
volatile GPIO_REGS* gpio1_regs = mmap(NULL, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0x44E07000);
close(fd); /* No need to keep fd open after mmap */
/* Check for MAP_FAILED error */
if (gpio1_regs != (GPIO_REGS*)-1) {
/* Do whatever */
(The last argument to mmap is where you place your hardware address, I'm using 0x44E07000 in this example.)
For further reading, see also:
https://man7.org/linux/man-pages/man2/mmap.2.html
https://pagefault.blog/2017/03/14/access-hardware-from-userspace-with-mmap/

Access data on sfr P1 using raw pointer

My Environment
IDE : Keil
Processor : AT89C4051 (Simulation Mode)
I'm trying to work with P1 register (address 0x90 specified by datasheet) by setting its value to all 0 (RESET state) and setting some specific pin to be 1 (SET state). I try this code
int main() {
*((unsigned char*)0x90) = 0;
while(1) {
*((unsigned char*)0x90) = 0xE0;
}
}
But nothing change.
When I use this example every work flawlessly
sfr P1 = 0x90;
int main()
{
P1 = 0;
P1 = 0xE0;
while(1);
}
My question is, what make the different between these code since it's all pointing at address 0x90 using sfr and unsigned char pointer.
You can't access SFRs of the 8051 series microcontroller by indirect accesses.
On devices with more than 128 bytes internal RAM, the I/O addresses 0x80 to 0xFF are used for both the SFRs and the upper half of the internal RAM:
SFRs are accessed by direct addressing only.
The internal RAM is accessed by indirect addressing only.
EDIT:
Source: Right on the product summary page there is the 8051 instruction set that documents this on the very first page.

Intel Galileo bare metal UART

I want to program a little "hello world" bare metal application on the Intel Galileo board. Using UEFI to print out text (to UART-1) works well, of course, but I want to access the UART "manually", without any help from UEFI.
In QEMU my code works well:
.h file
#define COM1_PORT (0x03F8)
#define UART_PORT (COM1_PORT)
enum uart_port_offs_t
{ // DLAB RW
THR = 0, // 0 W Transmitter Holding Buffer
RBR = 0, // 0 R Receiver Buffer
DLL = 0, // 1 RW Divisor Latch Low Byte
IER = 1, // 0 RW Interrupt Enable Register
DLH = 1, // 1 RW Divisor Latch High Byte
IIR = 2, // - R Interrupt Identification Register
FCR = 2, // - RW FIFO Control Register
LCR = 3, // - RW Line Control Register
MCR = 4, // - RW Modem Control Register
LSR = 5, // - R Line Status Register
MSR = 6, // - R Modem Status Register
SR = 7, // - RW Scratch Register
};
.c file
void uart_init(void)
{
outb(UART_PORT + IER, 0x00); // Disable all interrupts
outb(UART_PORT + LCR, LCR_DLAB);
outb(UART_PORT + DLL, BAUD_LL); // Set divisor (lo byte)
outb(UART_PORT + DLH, BAUD_HL); // (hi byte)
outb(UART_PORT + LCR, LCR_WORD_BITS_8 | LCR_PAR_NONE | LCR_STOP_BITS_1);
outb(UART_PORT + FCR, FCR_ENABLE | FCR_CLR_RECV | FCR_CLR_SEND | FCR_TRIGGER_16);
outb(UART_PORT + MCR, MCR_DSR | MCR_RTS | MCR_AUX2);
}
ssize_t uart_write(const char *buf, size_t len)
{
size_t written = 0;
while (written < len) {
while (!is_output_empty()) {
asm volatile ("pause");
}
outb(UART_PORT + THR, buf[written]);
++written;
}
return written;
}
main
SystemTable->ConOut->OutputString(SystemTable->ConOut, L"Exiting EFI boot services ...\r\n");
SystemTable->BootServices->ExitBootServices(ImageHandle, map_key);
uart_init();
while (1) {
const char s[] = "UART\r\n";
uart_write(s, sizeof (s) - 1);
}
The specs did not help me very much. I guess that the UARTs on the Intel Galileo board don't use/emulate the normal/legacy COM ports 3F8h, 2F8h, 3E8h, or 2E8h.
Can anyone tell me what I am doing wrong, or even post a minimal bare metal hello world example?
I assume you are aiming at the serial port that is the "audio-like" connector on the Intel Galileo board.
Here are a few resources that should help:
Galileo schematic
Intel Quark SoC X1000 datasheet
Intel Galileo IO Mapping
Sergey's blog entry about Configuring the Serial Port for Galileo
Intel Quark Board Support Package downloads, including
Board Support Package Sources (currently ver.1.0.0)
Intel Quark SoC X1000 UEFI Firmware Writer’s Guide
Things to note about this UART:
This serial port comes out of the QUARK chip as UART1 (see the schematics).
There are a few GPIOs that you may need to manipulate (see Sergey's blog for doing this in Linux):
gpio4: This GPIO controls level shifter for UART signals and some other signals connected to Quark SoC, such as SPI and fast I/O. Writing '1' to this GPIO enables level shifter.
gpio40: This GPIO controls multiplexer for pin 0. Writing '0' to this GPIO connects pin 0 to UART's RxD (receive data) signal.
gpio41: This GPIO controls multiplexer for pin 1. Writing '0' to this GPIO connects pin 1 to UART's TxD (transmit data) signal.
Check the chapter 18 (High Speed UART) in the Quark datasheet for what to put in the UART registers:
Registers DLH, DLL specify the baud rate
Decide whether you want the DMA mode (chapter 18.3.1), the FIFO-interrupt mode (chapter 18.3.2), or the FIFO-polling mode (chapter 18.3.3). The latter is simpler but less effective, IMHO. The former requires you to configure DMA properly as well.
Since there is quite a bit to read for chapter 18 (~67 pages of useful information), I'm not going to retype all that here, please read the datasheet and configure the registers accordingly.
General notes:
For bare-metal approach first make sure that your boot procedure is correct, configuring all the clocking options, GPIO default modes and values, timers if any, etc. For Boot checklist read chapter 4.12 in X1000 UEFI Firmware Writer’s Guide (~18 things to do to boot this chip). After that I'd verify it with a simple "LED blinking" application on a GPIO.
Tinkering with 3F8h and similar ports is not going to help on "bare metal" of this SoC. You need to deal with the registers directly, or find and use appropriate library or framework (maybe UEFI BIOS?).
Programming sources for the particular platform should be a good read for examples.
For example, in Board Support Package Sources for Intel Quark the archive Quark_EDKII_v1.0.0.tar.gz is the UEFI source code for Quark/Galileo. It there, the Serial.c and Serial.h files might just be what you are looking for:
Quark_EDKII_v1.0.0/QuarkSocPkg/QuarkSouthCluster/Uart/Dxe/Serial.*

Resources