mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/atxmega: Add external bus interface
Introduce XMEGA EBI driver. This enable EBI for use with all memory supported by the device and peripherals. It include support to SRAM, SDRAM, LCDs or any other external bus access. Note: This feature only works for A1/A1U series, which are, the series with EBI hardware. Signed-off-by: Gerson Fernando Budke <nandojve@gmail.com>
This commit is contained in:
parent
16f7c94875
commit
307e8c7a17
@ -130,6 +130,18 @@ static const spi_conf_t spi_config[] = {
|
||||
#define SPI_NUMOF ARRAY_SIZE(spi_config)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name EBI configuration
|
||||
*
|
||||
* For more information, see ebi_conf_t structure.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
static const ebi_conf_t ebi_config = {
|
||||
0
|
||||
};
|
||||
/** @} */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -26,6 +26,7 @@ config CPU_COMMON_ATXMEGA
|
||||
config CPU_CORE_ATXMEGA_A1
|
||||
bool
|
||||
select CPU_COMMON_ATXMEGA
|
||||
select HAS_ATXMEGA_EBI
|
||||
|
||||
config CPU_CORE_ATXMEGA_A3
|
||||
bool
|
||||
@ -76,6 +77,11 @@ source "$(RIOTCPU)/atxmega/Kconfig.XMEGAE"
|
||||
config HAS_CPU_ATXMEGA
|
||||
bool
|
||||
|
||||
config HAS_ATXMEGA_EBI
|
||||
bool
|
||||
help
|
||||
Indicates that the External Bus Interface is present.
|
||||
|
||||
config HAS_PERIPH_NVM
|
||||
bool
|
||||
help
|
||||
|
@ -4,4 +4,8 @@ MODULE = cpu
|
||||
# add a list of subdirectories, that should also be build
|
||||
DIRS = periph $(RIOTCPU)/avr8_common/
|
||||
|
||||
ifneq (,$(findstring a1,$(shell echo $(CPU_MODEL) | cut -c8-)))
|
||||
DIRS += ebi
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
@ -8,4 +8,8 @@ ifeq (,$(filter cpuid,$(USEMODULE)))
|
||||
USEMODULE += periph_nvm
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring a1,$(shell echo $(CPU_MODEL) | cut -c8-)))
|
||||
USEMODULE += atxmega_ebi
|
||||
endif
|
||||
|
||||
include $(RIOTCPU)/avr8_common/Makefile.dep
|
||||
|
@ -3,6 +3,10 @@ include $(RIOTCPU)/avr8_common/Makefile.features
|
||||
# common feature are defined in avr8_common/Makefile.features
|
||||
# Only add Additional features
|
||||
|
||||
ifneq (,$(findstring a1,$(shell echo $(CPU_MODEL) | cut -c8-)))
|
||||
FEATURES_PROVIDED += atxmega_ebi
|
||||
endif
|
||||
|
||||
FEATURES_PROVIDED += cpu_core_atxmega
|
||||
FEATURES_PROVIDED += periph_cpuid
|
||||
FEATURES_PROVIDED += periph_gpio periph_gpio_irq
|
||||
|
@ -1,5 +1,8 @@
|
||||
export CPU_ATXMEGA 1
|
||||
|
||||
# should expand RAM ?
|
||||
EXP_RAM = 0
|
||||
|
||||
# CPU ROM/RAM
|
||||
ifneq (,$(findstring atxmega8,$(CPU_MODEL)))
|
||||
RAM_LEN = 1K
|
||||
@ -14,13 +17,13 @@ ifneq (,$(findstring atxmega32,$(CPU_MODEL)))
|
||||
ROM_LEN = 32K
|
||||
endif
|
||||
ifneq (,$(findstring atxmega64,$(CPU_MODEL)))
|
||||
RAM_LEN ?= 4K
|
||||
RAM_LEN = 4K
|
||||
ROM_LEN = 64K
|
||||
endif
|
||||
#ifneq (,$(findstring atxmega128,$(CPU_MODEL)))
|
||||
RAM_LEN ?= 8K
|
||||
ifneq (,$(findstring atxmega128,$(CPU_MODEL)))
|
||||
RAM_LEN = 8K
|
||||
ROM_LEN = 128K
|
||||
#endif
|
||||
endif
|
||||
ifneq (,$(findstring atxmega192,$(CPU_MODEL)))
|
||||
RAM_LEN = 16K
|
||||
ROM_LEN = 192K
|
||||
@ -34,5 +37,14 @@ ifneq (,$(findstring atxmega384,$(CPU_MODEL)))
|
||||
ROM_LEN = 384K
|
||||
endif
|
||||
|
||||
ifneq (,$(findstring a1,$(shell echo $(CPU_MODEL) | cut -c8-)))
|
||||
ifeq ($(EXP_RAM),1)
|
||||
CFLAGS += -DRAM_LEN=$(RAM_LEN)-1
|
||||
LDSCRIPT_EXTRA = -Wl,--defsym=__heap_end=0x800000+$(RAM_LEN)-1
|
||||
endif
|
||||
endif
|
||||
|
||||
# CPU depends on the avr8 common module, so include it
|
||||
include $(RIOTCPU)/avr8_common/Makefile.include
|
||||
|
||||
PSEUDOMODULE += atxmega_ebi
|
||||
|
4
cpu/atxmega/ebi/Makefile
Normal file
4
cpu/atxmega/ebi/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
# define the module that is build
|
||||
MODULE = atxmega_ebi
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
375
cpu/atxmega/ebi/ebi.c
Normal file
375
cpu/atxmega/ebi/ebi.c
Normal file
@ -0,0 +1,375 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Gerson Fernando Budke <nandojve@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_atxmega
|
||||
* @ingroup cpu_atxmega_periph
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Low-level EBI (External BUS Interface) driver implementation
|
||||
*
|
||||
* @author Gerson Fernando Budke <nandojve@gmail.com>
|
||||
* https://www.avrfreaks.net/forum/xmega-ebi-and-sram
|
||||
* https://www.avrfreaks.net/forum/xmega-au-four-port-ebi
|
||||
* https://community.atmel.com/forum/location-variable-specified-address
|
||||
* @}
|
||||
*/
|
||||
#include <avr/io.h>
|
||||
|
||||
#include "assert.h"
|
||||
#include "periph_conf.h"
|
||||
#include "cpu_pm.h"
|
||||
#include "cpu_ebi.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
void ebi_init(void) __attribute__((naked, section(".init1"), used));
|
||||
|
||||
/**
|
||||
* @brief Set up the I/O ports for use by the EBI.
|
||||
*
|
||||
* @note In SDRAM mode the \a sram_ale and \a lpc_ale parameters are ignored
|
||||
* by the hardware.
|
||||
*/
|
||||
void ebi_init(void)
|
||||
{
|
||||
EBI_CS_t *cs;
|
||||
uint8_t mode;
|
||||
uint8_t sd_ctrl = 0;
|
||||
uint8_t i;
|
||||
uint8_t expand_sram = 0;
|
||||
|
||||
/*
|
||||
* This is a mandatory configuration. Whowever, to complete disable module
|
||||
* just configure it as:
|
||||
*
|
||||
* static const ebi_conf_t ebi_config = { 0 };
|
||||
*
|
||||
* or, for a temporary disable, set addr_bits to 0 at periph_conf.h:
|
||||
*
|
||||
* .addr_bits = 0,
|
||||
*/
|
||||
if (ebi_config.addr_bits == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set address and control lines as outputs, and active-low control lines
|
||||
* initially high.
|
||||
*/
|
||||
if (ebi_config.flags & EBI_PORT_SDRAM) {
|
||||
/* With SDRAM, the configuration is fairly fixed. */
|
||||
PORTH.OUT = 0x0f;
|
||||
PORTH.DIR = 0xff;
|
||||
PORTJ.DIR = 0xf0;
|
||||
PORTK.DIR = 0xff;
|
||||
} else {
|
||||
uint8_t ale_mask = ebi_config.sram_ale | ebi_config.lpc_ale;
|
||||
uint8_t port_mask;
|
||||
|
||||
/*
|
||||
* Set PORTH initial state, set WE and CAS/RE high by default.
|
||||
* Set chip selects high by default if enabled.
|
||||
*/
|
||||
port_mask = 0x03;
|
||||
if (ebi_config.flags & EBI_PORT_CS0) {
|
||||
port_mask |= 0x10;
|
||||
}
|
||||
if (ebi_config.flags & EBI_PORT_CS1) {
|
||||
port_mask |= 0x20;
|
||||
}
|
||||
if (ebi_config.flags & EBI_PORT_CS2) {
|
||||
port_mask |= 0x40;
|
||||
}
|
||||
if (ebi_config.flags & EBI_PORT_CS3) {
|
||||
port_mask |= 0x80;
|
||||
}
|
||||
PORTH.OUT = port_mask;
|
||||
|
||||
/*
|
||||
* Set PORTH direction, enable WE, CAS/RE and RAS/ALE1 to
|
||||
* output by default. Set chip select direction if enabled.
|
||||
*/
|
||||
port_mask = 0x07;
|
||||
|
||||
/* If two latches are in use, enable the ALE2 pin as well. */
|
||||
if (ale_mask & 0x02) {
|
||||
port_mask |= 0x08;
|
||||
}
|
||||
if (ebi_config.flags & EBI_PORT_CS0 || ebi_config.addr_bits > 16) {
|
||||
port_mask |= 0x10;
|
||||
}
|
||||
if (ebi_config.flags & EBI_PORT_CS1 || ebi_config.addr_bits > 17) {
|
||||
port_mask |= 0x20;
|
||||
}
|
||||
if (ebi_config.flags & EBI_PORT_CS2 || ebi_config.addr_bits > 18) {
|
||||
port_mask |= 0x40;
|
||||
}
|
||||
if (ebi_config.flags & EBI_PORT_CS3 || ebi_config.addr_bits > 19) {
|
||||
port_mask |= 0x80;
|
||||
}
|
||||
PORTH.DIR = port_mask;
|
||||
|
||||
/*
|
||||
* PORTJ is always used for data, direction and value is controlled by
|
||||
* the EBI module.
|
||||
*/
|
||||
|
||||
/* PORTK is only used in 3-port mode */
|
||||
if (ebi_config.flags & EBI_PORT_3PORT) {
|
||||
port_mask = 0x00;
|
||||
|
||||
if (ebi_config.flags & EBI_PORT_SRAM) {
|
||||
/*
|
||||
* Bits 0..7 go here, so if we have 8 lines or more, enable all
|
||||
* lines. Otherwise, enable as many as we need.
|
||||
*/
|
||||
if (ebi_config.addr_bits < 8) {
|
||||
port_mask = (1 << ebi_config.addr_bits) - 1;
|
||||
}
|
||||
else {
|
||||
port_mask = 0xff;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Bits 8..15 go here, so if we have less than 16 address lines,
|
||||
* disable the ones that we don't need. If we have 8 lines or
|
||||
* less, disable all address lines on this port.
|
||||
*/
|
||||
if (ebi_config.addr_bits <= 8) {
|
||||
port_mask = 0x00;
|
||||
}
|
||||
else if (ebi_config.addr_bits < 16) {
|
||||
port_mask = (1 << (ebi_config.addr_bits - 8)) - 1;
|
||||
}
|
||||
else {
|
||||
port_mask = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
PORTK.DIR = port_mask;
|
||||
}
|
||||
}
|
||||
|
||||
if (ebi_config.flags & EBI_PORT_3PORT) {
|
||||
mode = EBI_IFMODE_3PORT_gc;
|
||||
}
|
||||
else {
|
||||
mode = EBI_IFMODE_2PORT_gc;
|
||||
}
|
||||
|
||||
if (ebi_config.sram_ale == 1) {
|
||||
mode |= EBI_SRMODE_ALE1_gc;
|
||||
}
|
||||
else if (ebi_config.sram_ale == 2) {
|
||||
mode |= EBI_SRMODE_ALE12_gc;
|
||||
}
|
||||
else {
|
||||
mode |= EBI_SRMODE_NOALE_gc;
|
||||
}
|
||||
|
||||
if (ebi_config.lpc_ale > 0) {
|
||||
mode |= (ebi_config.lpc_ale << EBI_LPCMODE_gp);
|
||||
}
|
||||
|
||||
if (ebi_config.sdram.cas_latency == EBI_SDRAM_CAS_LAT_3CLK) {
|
||||
sd_ctrl |= EBI_SDCAS_bm;
|
||||
}
|
||||
|
||||
if (ebi_config.sdram.row_bits == EBI_SDRAM_ROW_BITS_12) {
|
||||
sd_ctrl |= EBI_SDROW_bm;
|
||||
}
|
||||
|
||||
/* Enable EBI periph clock */
|
||||
PR.PRGEN &= ~PR_EBI_bm;
|
||||
|
||||
/* 8-bit SDRAM requires 4-port EBI, which we don't have. */
|
||||
EBI.CTRL = EBI_SDDATAW_4BIT_gc
|
||||
| mode;
|
||||
EBI.SDRAMCTRLA = sd_ctrl
|
||||
| ebi_config.sdram.column_bits;
|
||||
EBI.SDRAMCTRLB = ebi_config.sdram.ld_mode_dly
|
||||
| ebi_config.sdram.row_cycle_dly
|
||||
| ebi_config.sdram.row_prechage_dly;
|
||||
EBI.SDRAMCTRLC = ebi_config.sdram.write_recovery_dly
|
||||
| ebi_config.sdram.exit_self_rfsh_dly
|
||||
| ebi_config.sdram.row_to_column_dly;
|
||||
EBI.REFRESH = ebi_config.sdram.refresh_period & 0x0FFF;
|
||||
EBI.INITDLY = ebi_config.sdram.init_dly & 0x3FFF;
|
||||
|
||||
/* IRQ are disabled here */
|
||||
cs = (EBI_CS_t *)&EBI.CS0;
|
||||
for (i = 0; i < PERIPH_EBI_MAX_CS; i++) {
|
||||
if (ebi_config.cs[i].mode != EBI_CS_MODE_DISABLED_gc &&
|
||||
ebi_config.cs[i].mode != EBI_CS_MODE_SDRAM_gc) {
|
||||
|
||||
/* Configure */
|
||||
cs[i].CTRLA = ebi_config.cs[i].space;
|
||||
cs[i].CTRLB = ebi_config.cs[i].wait;
|
||||
cs[i].BASEADDR = ((ebi_config.cs[i].address >> 8) & 0xfff0);
|
||||
|
||||
/* Enable */
|
||||
cs[i].CTRLA = ebi_config.cs[i].space | ebi_config.cs[i].mode;
|
||||
|
||||
if (ebi_config.cs[i].address == 0) {
|
||||
expand_sram = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only CS[3] can be configured as SDRAM.
|
||||
* CS structure is little bit different too.
|
||||
*/
|
||||
if (ebi_config.cs[3].mode == EBI_CS_MODE_SDRAM_gc) {
|
||||
cs[3].CTRLA = ebi_config.cs[3].space;
|
||||
cs[3].CTRLB = ebi_config.sdram.mode
|
||||
| (ebi_config.sdram.refresh ? EBI_CS_SDSREN_bm : 0);
|
||||
cs[3].BASEADDR = ((ebi_config.cs[3].address >> 8) & 0xfff0);
|
||||
|
||||
cs[3].CTRLA = ebi_config.cs[3].space | ebi_config.cs[3].mode;
|
||||
|
||||
if (ebi_config.cs[3].address == 0) {
|
||||
expand_sram = 1;
|
||||
}
|
||||
|
||||
while (!(cs[3].CTRLB & EBI_CS_SDINITDONE_bm)) {};
|
||||
}
|
||||
|
||||
if (expand_sram > 0) {
|
||||
/**
|
||||
* @brief Set new Stack Pointer
|
||||
*/
|
||||
__asm__ volatile (
|
||||
"out __SP_L__, %A[stack] \n\t"
|
||||
"out __SP_H__, %B[stack] \n\t"
|
||||
: /* no output */
|
||||
: [stack] "r"(RAM_LEN)
|
||||
: "memory"
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
uint16_t hugemem_read16(const hugemem_ptr_t from)
|
||||
{
|
||||
uint16_t value;
|
||||
|
||||
__asm__ volatile (
|
||||
"movw r30, %A[from] \n\t"
|
||||
"out %[rampz], %C[from] \n\t"
|
||||
"ld %A[dest], Z+ \n\t"
|
||||
"ld %B[dest], Z \n\t"
|
||||
"out %[rampz], __zero_reg__ \n\t"
|
||||
: [dest] "=r"(value)
|
||||
: [from] "r"(from),
|
||||
[rampz] "i"(&RAMPZ)
|
||||
: "r30", "r31"
|
||||
);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void hugemem_write16(hugemem_ptr_t to, uint16_t val)
|
||||
{
|
||||
__asm__ volatile (
|
||||
"movw r30, %A[to] \n\t"
|
||||
"out %[rampz], %C[to] \n\t"
|
||||
"st Z+, %A[val] \n\t"
|
||||
"st Z, %B[val] \n\t"
|
||||
"out %[rampz], __zero_reg__ \n\t"
|
||||
: /* no output */
|
||||
: [to] "r"(to),
|
||||
[val] "r"(val),
|
||||
[rampz] "i"(&RAMPZ)
|
||||
: "r30", "r31"
|
||||
);
|
||||
}
|
||||
|
||||
uint32_t hugemem_read32(const hugemem_ptr_t from)
|
||||
{
|
||||
uint32_t value;
|
||||
|
||||
__asm__ volatile (
|
||||
"movw r30, %A[from] \n\t"
|
||||
"out %[rampz], %C[from] \n\t"
|
||||
"ld %A[dest], Z+ \n\t"
|
||||
"ld %B[dest], Z+ \n\t"
|
||||
"ld %C[dest], Z+ \n\t"
|
||||
"ld %D[dest], Z \n\t"
|
||||
"out %[rampz], __zero_reg__ \n\t"
|
||||
: [dest] "=r"(value)
|
||||
: [from] "r"(from),
|
||||
[rampz] "i"(&RAMPZ)
|
||||
: "r30", "r31"
|
||||
);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void hugemem_write32(hugemem_ptr_t to, uint32_t val)
|
||||
{
|
||||
__asm__ volatile (
|
||||
"movw r30, %A[to] \n\t"
|
||||
"out %[rampz], %C[to] \n\t"
|
||||
"st Z+, %A[val] \n\t"
|
||||
"st Z+, %B[val] \n\t"
|
||||
"st Z+, %C[val] \n\t"
|
||||
"st Z, %D[val] \n\t"
|
||||
"out %[rampz], __zero_reg__ \n\t"
|
||||
: /* no output */
|
||||
: [to] "r"(to),
|
||||
[val] "r"(val),
|
||||
[rampz] "i"(&RAMPZ)
|
||||
: "r30", "r31"
|
||||
);
|
||||
}
|
||||
|
||||
void hugemem_read_block(void *to, const hugemem_ptr_t from, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
__asm__ volatile (
|
||||
"movw r30, %A[from] \n\t"
|
||||
"out %[rampz], %C[from] \n\t"
|
||||
"get_%=: \n\t"
|
||||
"ld __tmp_reg__, Z+ \n\t"
|
||||
"st X+, __tmp_reg__ \n\t"
|
||||
"sbiw %A[size], 1 \n\t"
|
||||
"brne get_%= \n\t"
|
||||
"out %[rampz], __zero_reg__ \n\t"
|
||||
: [to] "+x"(to),
|
||||
[size] "+w"(size)
|
||||
: [from] "r"(from),
|
||||
[rampz] "i"(&RAMPZ)
|
||||
: "r30", "r31"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void hugemem_write_block(hugemem_ptr_t to, const void *from, size_t size)
|
||||
{
|
||||
if (size > 0) {
|
||||
__asm__ volatile (
|
||||
"movw r30, %A[from] \n\t"
|
||||
"out %[rampz], %C[from] \n\t"
|
||||
"put_%=: \n\t"
|
||||
"ld __tmp_reg__, X+ \n\t"
|
||||
"st Z+, __tmp_reg__ \n\t"
|
||||
"sbiw %A[size], 1 \n\t"
|
||||
"brne put_%= \n\t"
|
||||
"out %[rampz], __zero_reg__ \n\t"
|
||||
: [from] "+x"(from),
|
||||
[size] "+w"(size)
|
||||
: [to] "r"(to),
|
||||
[rampz] "i"(&RAMPZ)
|
||||
: "r30", "r31"
|
||||
);
|
||||
}
|
||||
}
|
154
cpu/atxmega/include/cpu_ebi.h
Normal file
154
cpu/atxmega/include/cpu_ebi.h
Normal file
@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright (C) 2021 Gerson Fernando Budke
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup cpu_atxmega
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief External Bus Interface API
|
||||
*
|
||||
* @author Gerson Fernando Budke <nandojve@gmail.com>
|
||||
*/
|
||||
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifndef CPU_EBI_H
|
||||
#define CPU_EBI_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef uint32_t hugemem_ptr_t;
|
||||
|
||||
#define HUGEMEM_NULL 0
|
||||
|
||||
void ebi_init(void);
|
||||
|
||||
/**
|
||||
* @brief Load byte register from external memory pointer
|
||||
*
|
||||
* @param from 24-bit external memory address
|
||||
* @return uint8_t 8-bit value
|
||||
*/
|
||||
static inline uint8_t hugemem_read8(const hugemem_ptr_t from)
|
||||
{
|
||||
uint8_t value;
|
||||
|
||||
__asm__ volatile (
|
||||
/* place 24 bit address into r30, r31, RAMPZ
|
||||
* r31:r30 <- %B[from]:%A[from]
|
||||
* RAMPZ <- %C[from]
|
||||
*/
|
||||
"movw r30, %A[from] \n\t"
|
||||
"out %[rampz], %C[from] \n\t"
|
||||
/* read byte from 24 bit address
|
||||
* register <- *(RAMPZ + Z)
|
||||
*/
|
||||
"ld %[dest], Z \n\t"
|
||||
/* clear ramp */
|
||||
"out %[rampz], __zero_reg__ \n\t"
|
||||
: [dest] "=r"(value)
|
||||
: [from] "r"(from),
|
||||
[rampz] "i"(&RAMPZ)
|
||||
: "r30", "r31"
|
||||
);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Store byte register to external memory pointer
|
||||
*
|
||||
* @param to 24-bit external memory address
|
||||
* @param val 8-bit value
|
||||
*/
|
||||
static inline void hugemem_write8(hugemem_ptr_t to, uint8_t val)
|
||||
{
|
||||
__asm__ volatile (
|
||||
/* place 24 bit address into r30, r31, RAMPZ
|
||||
* r31:r30 <- %B[from]:%A[from]
|
||||
* RAMPZ <- %C[from]
|
||||
*/
|
||||
"movw r30, %A[to] \n\t"
|
||||
"out %[rampz], %C[to] \n\t"
|
||||
/* write byte to 24 bit address
|
||||
* (RAMPZ + Z) <- register
|
||||
*/
|
||||
"st Z, %[val] \n\t"
|
||||
/* clear ramp */
|
||||
"out %[rampz], __zero_reg__ \n\t"
|
||||
:
|
||||
: [to] "r"(to),
|
||||
[val] "r"(val),
|
||||
[rampz] "i"(&RAMPZ)
|
||||
: "r30", "r31"
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load register pair from external memory pointer
|
||||
*
|
||||
* @param from 24-bit external memory address
|
||||
* @return uint16_t 8-bit value
|
||||
*/
|
||||
uint16_t hugemem_read16(const hugemem_ptr_t from);
|
||||
|
||||
/**
|
||||
* @brief Store register pair to external memory pointer
|
||||
*
|
||||
* @param to 24-bit external memory address
|
||||
* @param val 16-bit value
|
||||
*/
|
||||
void hugemem_write16(hugemem_ptr_t to, uint16_t val);
|
||||
|
||||
/**
|
||||
* @brief Load two register pair from external memory pointer
|
||||
*
|
||||
* @param from 24-bit external memory address
|
||||
* @return uint8_t 32-bit value
|
||||
*/
|
||||
uint32_t hugemem_read32(const hugemem_ptr_t from);
|
||||
|
||||
/**
|
||||
* @brief Store two register pair to external memory pointer
|
||||
*
|
||||
* @param to 24-bit external memory address
|
||||
* @param val 32-bit value
|
||||
*/
|
||||
void hugemem_write32(hugemem_ptr_t to, uint32_t val);
|
||||
|
||||
/**
|
||||
* @brief Read byte stream from external memory to internal memory
|
||||
*
|
||||
* @param to intenal memory pointer
|
||||
* @param from 24-bit external memory pointer
|
||||
* @param size number of bytes to read
|
||||
*
|
||||
* @note The address range to copy from is within 64 kB boundary
|
||||
*/
|
||||
void hugemem_read_block(void *to, const hugemem_ptr_t from, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Write byte stream from internal memory to external memory
|
||||
*
|
||||
* @param to 24-bit external memory pointer
|
||||
* @param from intenal memory pointer
|
||||
* @param size number of bytes to write
|
||||
*
|
||||
* @note The address range to copy to is within 64 kB boundary
|
||||
*/
|
||||
void hugemem_write_block(hugemem_ptr_t to, const void *from, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CPU_EBI_H */
|
||||
/** @} */
|
@ -354,6 +354,236 @@ typedef enum {
|
||||
} spi_clk_t;
|
||||
/** @} */
|
||||
|
||||
#if defined(__AVR_ATxmega64A1__) || \
|
||||
defined(__AVR_ATxmega128A1__) || \
|
||||
defined(__AVR_ATxmega64A1U__) || \
|
||||
defined(__AVR_ATxmega128A1U__) || \
|
||||
defined(Doxygen)
|
||||
/**
|
||||
* @brief EBI (External Bus Interface)
|
||||
* {@
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief EBI Low Pin Count (LPC) Mode Address Latch Enable (ALE) config
|
||||
*/
|
||||
typedef enum {
|
||||
EBI_LPC_MODE_ALE1 = 0x01, /**< Data multiplexed with Address byte 0 */
|
||||
EBI_LPC_MODE_ALE12 = 0x03, /**< Data multiplexed with Address byte 0 and 1 */
|
||||
} ebi_lpc_mode_t;
|
||||
|
||||
/**
|
||||
* @brief EBI Port Access Flags
|
||||
*
|
||||
* Indicate what should be configured
|
||||
*/
|
||||
typedef enum {
|
||||
EBI_PORT_3PORT = 0x01, /**< Three Port Config */
|
||||
EBI_PORT_SDRAM = 0x02, /**< SDRAM Port Config */
|
||||
EBI_PORT_SRAM = 0x04, /**< SRAM Port Config */
|
||||
EBI_PORT_LPC = 0x08, /**< Low Pin Count Port Config */
|
||||
EBI_PORT_CS0 = 0x10, /**< Chip Select 0 Config */
|
||||
EBI_PORT_CS1 = 0x20, /**< Chip Select 1 Config */
|
||||
EBI_PORT_CS2 = 0x40, /**< Chip Select 2 Config */
|
||||
EBI_PORT_CS3 = 0x80, /**< Chip Select 3 Config */
|
||||
EBI_PORT_CS_ALL = 0xF0, /**< Chip Select 0-3 Config */
|
||||
} ebi_port_mask_t;
|
||||
|
||||
/**
|
||||
* @brief SDRAM Column Address Strobe latency
|
||||
*/
|
||||
typedef enum {
|
||||
EBI_SDRAM_CAS_LAT_2CLK = 0x00, /**< 2 Clk PER2 cycles delay */
|
||||
EBI_SDRAM_CAS_LAT_3CLK = 0x01, /**< 3 Clk PER2 cycles delay */
|
||||
} ebi_sdram_cas_latency_t;
|
||||
|
||||
/**
|
||||
* @brief SDRAM number of Row Bits
|
||||
*/
|
||||
typedef enum {
|
||||
EBI_SDRAM_ROW_BITS_11 = 0x00, /**< 11 row bits */
|
||||
EBI_SDRAM_ROW_BITS_12 = 0x01, /**< 12 row bits */
|
||||
} ebi_sdram_row_bits_t;
|
||||
|
||||
/**
|
||||
* @brief EBI maximum Chip Select
|
||||
*/
|
||||
#define PERIPH_EBI_MAX_CS (4)
|
||||
|
||||
/**
|
||||
* @brief EBI SDRAM Chip Select
|
||||
*/
|
||||
#define PERIPH_EBI_SDRAM_CS (3)
|
||||
|
||||
/**
|
||||
* @brief EBI Chip Select configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
EBI_CS_MODE_t mode; /**< Chip Select address mode */
|
||||
#if defined (__AVR_ATxmega64A1U__) || defined (__AVR_ATxmega128A1U__)
|
||||
EBI_CS_ASPACE_t space; /**< Chip Select address space */
|
||||
#else
|
||||
EBI_CS_ASIZE_t space; /**< Chip Select address space */
|
||||
#endif
|
||||
EBI_CS_SRWS_t wait; /**< SRAM Wait State Selection */
|
||||
uint32_t address; /**< Chip Select Base Address */
|
||||
} ebi_cs_t;
|
||||
|
||||
/**
|
||||
* @brief EBI SDRAM configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t refresh; /**< Self-Refresh Enabled */
|
||||
uint16_t refresh_period; /**< microseconds */
|
||||
uint16_t init_dly; /**< microseconds */
|
||||
EBI_CS_SDMODE_t mode; /**< Access Mode */
|
||||
ebi_sdram_cas_latency_t cas_latency; /**< CAS Latency */
|
||||
ebi_sdram_row_bits_t row_bits; /**< ROW bits */
|
||||
EBI_SDCOL_t column_bits; /**< COLUMN bits */
|
||||
EBI_MRDLY_t ld_mode_dly; /**< Number of Clk PER2 cycles */
|
||||
EBI_ROWCYCDLY_t row_cycle_dly; /**< Number of Clk PER2 cycles */
|
||||
EBI_RPDLY_t row_prechage_dly; /**< Number of Clk PER2 cycles */
|
||||
EBI_WRDLY_t write_recovery_dly; /**< Number of Clk PER2 cycles */
|
||||
EBI_ESRDLY_t exit_self_rfsh_dly; /**< Number of Clk PER2 cycles */
|
||||
EBI_ROWCOLDLY_t row_to_column_dly; /**< Number of Clk PER2 cycles */
|
||||
} ebi_sd_t;
|
||||
|
||||
/**
|
||||
* @brief EBI configuration structure
|
||||
*
|
||||
* The ebi_conf_t define the whole external memory that ATxmega A1 can address.
|
||||
* It is recommended read all EBI chapter from XMEGA-AU manual.
|
||||
*
|
||||
* The external address space can be used to address external peripherals and
|
||||
* expand SRAM. The ebi driver provide methods to read/write in external
|
||||
* address space. The SRAM can be expanded up to 64k when one chip select
|
||||
* address have value equal to zero. To allow expand external RAM both
|
||||
* @b RAM_LEN and @b EXP_RAM variables should be override at board
|
||||
* makefile.include file.
|
||||
*
|
||||
* When expanding RAM for use with RIOT-OS, the memory address must be aligned
|
||||
* at external memory size boundary, respecting power of 2. In this case, to
|
||||
* add 32K memory on the system, the chip select address should be set to 0,
|
||||
* or 32k, or 64 etc. This means that when the board have external memory and
|
||||
* user wants to map part of that memory to expand RAM, both internal and
|
||||
* external memories must have same start address @b A, inclusive, see image
|
||||
* for details. This is necessary to make sure RIOT-OS have a contiguous address
|
||||
* space. The drawback is that first @b Xk external memory will be lost.
|
||||
* Assuming internal top address memory is @b B and external top address memory
|
||||
* is @b C. The XMEGA will automatically access internal memory <b><= B</b>
|
||||
* when address collide with external memory. At address <b>>= B + 1</b>, XMEGA
|
||||
* will access memory thru EBI. Also, note that @b RAM_LEN must be a power
|
||||
* of 2, so it can't e.g. be 48k.
|
||||
*
|
||||
* C ------
|
||||
* | |
|
||||
* | |
|
||||
* | |
|
||||
* B -----| |
|
||||
* | | |
|
||||
* | | |
|
||||
* A -----------
|
||||
*
|
||||
* @note To avoid parser problems, @b RAM_LEN must be defined as decimal value.
|
||||
*
|
||||
* Example: Add 256K of external RAM
|
||||
*
|
||||
* The max addressable RAM by SP is 64K due to limit of 16 bits. In this case,
|
||||
* RAM will be 64K. The remaining RAM can be addressed only by ebi_mem methods
|
||||
* and GCC doesn't see it.
|
||||
*
|
||||
* At board/periph_conf.h:
|
||||
*
|
||||
* static const ebi_conf_t ebi_config = {
|
||||
* ...
|
||||
* .cs = {
|
||||
* { EBI_CS_MODE_DISABLED_gc,
|
||||
* 0,
|
||||
* EBI_CS_SRWS_0CLK_gc,
|
||||
* 0x0UL,
|
||||
* },
|
||||
* { EBI_CS_MODE_DISABLED_gc,
|
||||
* 0,
|
||||
* EBI_CS_SRWS_0CLK_gc,
|
||||
* 0x0UL,
|
||||
* },
|
||||
* { EBI_CS_MODE_LPC_gc,
|
||||
* EBI_CS_ASPACE_256KB_gc,
|
||||
* EBI_CS_SRWS_1CLK_gc,
|
||||
* 0x0UL,
|
||||
* },
|
||||
* { EBI_CS_MODE_DISABLED_gc,
|
||||
* 0,
|
||||
* EBI_CS_SRWS_0CLK_gc,
|
||||
* 0x0UL,
|
||||
* },
|
||||
* },
|
||||
* };
|
||||
*
|
||||
* At board/Makefile.include:
|
||||
* override RAM_LEN = 65536
|
||||
* override EXP_RAM = 1
|
||||
*
|
||||
* Example: Add 32K of external RAM and a LCD
|
||||
*
|
||||
* At board/periph_conf.h:
|
||||
*
|
||||
* static const ebi_conf_t ebi_config = {
|
||||
* ...
|
||||
* .cs = {
|
||||
* { EBI_CS_MODE_DISABLED_gc,
|
||||
* 0,
|
||||
* EBI_CS_SRWS_0CLK_gc,
|
||||
* 0x0UL,
|
||||
* },
|
||||
* { EBI_CS_MODE_DISABLED_gc,
|
||||
* 0,
|
||||
* EBI_CS_SRWS_0CLK_gc,
|
||||
* 0x0UL,
|
||||
* },
|
||||
* { EBI_CS_MODE_LPC_gc,
|
||||
* EBI_CS_ASPACE_32KB_gc,
|
||||
* EBI_CS_SRWS_1CLK_gc,
|
||||
* 0x0UL,
|
||||
* },
|
||||
* { EBI_CS_MODE_LPC_gc,
|
||||
* EBI_CS_ASPACE_256B_gc,
|
||||
* EBI_CS_SRWS_5CLK_gc,
|
||||
* 0x100000UL,
|
||||
* },
|
||||
* },
|
||||
* };
|
||||
*
|
||||
* At board/Makefile.include:
|
||||
* override RAM_LEN = 32768
|
||||
* override EXP_RAM = 1
|
||||
*
|
||||
* This data structure a mandatory configuration for A1 variation. If no
|
||||
* external memory is used the module can be disabled defining data struct
|
||||
* with all zeros, as below:
|
||||
*
|
||||
* static const ebi_conf_t ebi_config = { 0 };
|
||||
*
|
||||
* or, for a temporary disable, set addr_bits to 0 at periph_conf.h:
|
||||
*
|
||||
* static const ebi_conf_t ebi_config = {
|
||||
* .addr_bits = 0,
|
||||
* ...
|
||||
* };
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t addr_bits; /**< EBI port address lines */
|
||||
ebi_port_mask_t flags; /**< EBI port flags */
|
||||
uint8_t sram_ale; /**< Number of ALE for SRAM mode */
|
||||
uint8_t lpc_ale; /**< Number of ALE for LPC mode */
|
||||
ebi_sd_t sdram; /**< SDRAM configuration */
|
||||
ebi_cs_t cs[PERIPH_EBI_MAX_CS]; /**< Chip Select configuration */
|
||||
} ebi_conf_t;
|
||||
/** @} */
|
||||
|
||||
#endif /* __AVR_ATxmegaxxxA1x__ */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -135,4 +135,14 @@ void pm_periph_power_off(void)
|
||||
for (i = 0; i <= 7; i++) {
|
||||
reg[i] = 0xff;
|
||||
}
|
||||
|
||||
/* EBI Must be always enabled when configured */
|
||||
#if defined (__AVR_ATxmega64A1__) || \
|
||||
defined (__AVR_ATxmega64A1U__) || \
|
||||
defined (__AVR_ATxmega128A1__) || \
|
||||
defined (__AVR_ATxmega128A1U__)
|
||||
if (ebi_config.addr_bits > 0) {
|
||||
reg[0] &= ~PR_EBI_bm;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ $(if $(ROM_LEN),,$(error ROM_LEN is not defined))
|
||||
$(if $(RAM_LEN),,$(error RAM_LEN is not defined))
|
||||
LINKFLAGS += $(LINKFLAGPREFIX)--defsym=__TEXT_REGION_LENGTH__=$(ROM_LEN)$(if $(ROM_RESERVED),-$(ROM_RESERVED))
|
||||
LINKFLAGS += $(LINKFLAGPREFIX)--defsym=__DATA_REGION_LENGTH__=$(RAM_LEN)
|
||||
LINKFLAGS += $(LDSCRIPT_EXTRA)
|
||||
|
||||
ifeq ($(LTO),1)
|
||||
# avr-gcc <4.8.3 has a bug when using LTO which causes a warning to be printed always:
|
||||
|
Loading…
Reference in New Issue
Block a user