8051 Development Tips

1.Compile environment

  • Toolchain Install:

SDCC:

1、apt install sdcc

2、https://sourceforge.net/projects/sdcc/files/ Download the version for the current compilation platform

If use the second method, you need to modify the PREFIX parameter of the makefile to the path of the bin folder of sdcc

#PREFIX =

PREFIX = ../sdcc-4.0.0/bin/

 

KEIL:

Compile the normal environment, directly open the keil project file under the __project_normal, and compile the standby, then open the __project_standby project file

  • Compile

SDCC:

Compile the normal environment,run the “make” cmd 

Compile the standby environment,run the “make STANDBY=1” cmd

KEIL:

Click the build button directly on the project

 

  • output

iopnormal.c/iopstandby.c file will be generated in the out directory After compilation.

Replace this two files with the same files under linux/kernel/drivers/misc/iop/.

 

2.Makefile

  • SDCC_NOGENRAMCLEAR

Adding the SDCC_NOGENRAMCLEAR environment variable during the compilation phase can skip the action of clearing IRAM during the system startup phase, mainly to avoid clearing the initialization data of RISC passed to IOP through IOP_DATA (for example, the i2c slave address passed to IOP through IOP_DATA0), this is a requirement of SDCC.

  • Memory mode

The standby mode needs to work in ICache in the case of power failure, so the small memory mode is adopted, and the variables and function parameters are declared as DATA type by default

In normal mode, the large mode is adopted, and variables and function parameters are declared as XDATA by default and stored in external RAM.

  • Xram size

Standby mode works in Icache, variables and parameters are stored in DATA, XDATA is not needed, so define xram-size as 0

Normal mode is large model, variables and parameters are stored in XDATA, and XDATA size is defined as 64k.

  • Bin file

Standby mode works in Icache, and the size of Icache is 16k, so the standby code is limited to 16k.

The code size of normal mode is limited to 64k.

 

3.standby

Standby work mode

After power-on reset, SP7021 will enter S0 in which all modules of SP7021 are powered. This is a state of full power and function.

 

In S1, IOP, SRAM (for storing program code of IOP), GPIO IV_MX0~IV_MX6 are powered. IOP runs ‘standby mode’ code stored in SRAM. Only GPIO pins, IV_MX0 ~ IV_MX6, can be accessed by IOP via its GPIO0 ~ GPIO6. Standby mode code is designed to detected some wake-up events, like power-on key signal from remote controller, special pattern of UART signal and etc. Once wake-up event is detected, IOP sets register to power on whole system (switch to S0).

In S3, only SRAM and Wake-up circuitry are powered. This is the minimum power-consumption state. In this state, IOP is power-off, but ‘standby mode’ code of IOP is preserved in SRAM. Wake-up circuitry will detect the state of WAKE_IN pin continuously. A LOW state in WAKE_IN pin will switch power state of SP7021 to S0.

 

  • Mode switch

S0 into S1mode command :cat /sys/devices/platform/soc@B/9c000400.iop/S1mode

S0 into S3mode command :poweroff

 

  • Code function introduction

 

  1. IOP and RISC synchronization

IOP code:

RISC code

Used to synchronize the IOP and RISC. When the IOP enters standby, it will notify RISC, and then RISC will issue an instruction to notify the IOP to enter S1mode/S3mode.

 

2、Instruction flow

RISC writes different cmd through IOP_DATA1_L to make the IOP enter a different mode. The code will be moved to the ICache first, and then the power will be cut off.

 

3、Load code into ICache

Fill_ICache: Before entering standby and poweroff, the standby code needs to be moved to Icache for execution. After reading the entire 16k code area, the code enters ICACHE for execution.

Because tmpx is only used to read the code area data, and tmpx is not used, volatile modification is required when defining tmpx variables, otherwise SDCC will optimize the code inside the for loop.

 

4、wakeup

By checking the IR and RTC interrupt flags in the IR_RTC_Status status, it can be judged whether to enter the wake-up state.

 

RISC sets the RTC timer, PMC_SYS_RTC_LATCH will be generated in the 0x3D register of the IOP after the timer expires, and the bit change of IOP polling determines whether to wake up

In addition to the above-mentioned judgment IR_RTC_Status, S1mode wake-up can also wake up the system through the input status of GPIO_IV_MX0--IV_MX6 (S3mode GPIO is not work).

 

4.Normal

  • crtstart.asm

crtstart.asm is the SDCC startup file. The location of start_stack is modified mainly to avoid conflicts between the system default stack location and 8051 registers. Sunplus 8051 is a special register at 0x30-0x80. Some registers cannot be overwritten, but general 8051 single-chip microcomputer 0x30--0x80 is the RAM area, which can be used casually.

So the stack is defined at the position of 0x90 because the special registers in the area between 0x90--0x80 are not used and can be directly overwritten

  • interrupt

SDCC requires that the interrupt handling function must be declared. If it is not declared, it will not be linked to the bin file. (keil does not have this restriction)

  • Function Description

  1. Mailbox

IOP and RISC can communicate data through IOP_DATA, IOP can directly access IOP_DATA0_L(0x50)--IOP_DATA11_H(0x67) 24 registers, each register is 8bit.

RISC directly accesses Group[8].8(0x9c000420)--Group[8].19(0x9c00044c), and RISC accesses each register for 16 bits.

IOP: IOP_DATA0_L = 0x12;

IOP_DATA0_H = 0x34;

RISC: read address 0x9c000420,the result is 0x3412.

2. Timer

The 8051 implements the above timers by default. You can also modify the TH/TL/TMOD parameters in timer.c/Int_Vec.c to configure different timing times according to your own needs . timer0/timer1 can only work in 1 mode at the same time. timer0 is used to realize the data transfer of printf.

 

timer0 responds in timer0_isr interrupt function.

Timer1 responds in timer1_isr interrupt function.

EXT_TIMER1/2 responds in ex0_isr interrupt function, the interrupt is distinguished by the bit of the IntFlag register(0x39).

 

3. GPIO Control

To control GPIO in IOP, need to set Group[6]/Group[11] in RISC, and set IO port to GPIO mode/ IOP control.

  • Configure Input/Output mode:

GPIO_IO0_CFG(0xB0)-- GPIO_IO15_CFG(0x9F);

Each register is 8bit, each bit represents an IO port status, bit is set to 1 as output mode, and 0 is set as input mode

  • Get Input status:

GPIO_PORT0_IN(0xD4)--GPIO_PORT15_IN(0xEF);

Each bit represents the state of an io port, bit is 1 to indicate that the input is high, and bit is 0 to indicate that the input is low. (It needs to be used after being configured as input mode)

  • Output settings:

GPIO_PORT0_OUT(0xC0)--GPIO_PORT15_OUT(0xCF);

Each bit represents an io port status, bit setting 1 means outputting high level, bit setting 0 means outputting low level. (It needs to be used after being configured as output mode)

  • For example:

set GPIO_P1_03(G_MX11) to Output mode, and set it to high level output:

1. G101.25 |= (0x1<<11); // RISC setting, IO port is in GPIO mode

2. G6.0 = (0x1<<(11+16))|(0x0<<11); // RISC setting, IOP control

3. GPIO_IO1_CFG |= (0x1<<3); // IOP setting, output mode

4. GPIO_PORT1_OUT |= (0x1<<3); // IOP setting, output high level

 

Set GPIO_P2_04(G_MX20) to input mode:

1. G101.25 |= (0x1<<20); // RISC setting, IO port is in GPIO mode

2. G6.1 = (0x1<<(4+16))|(0x0<<4); // RISC setting, IOP control

3. GPIO_IO2_CFG &= ~(0x1<<4); // IOP setting, output mode

4. If((GPIO_PORT2_IN & (0x1<<4)) == (0x1<<4)) //IOP setting, output high level

//input high level ;

else

//input low level ;

4. delay

Realize 2 delay functions (1us/1ms), delay through program execution time, can directly call the function interface to realize the delay function.

 

void delay_1us(unsigned int n);

void delay_1ms(unsigned int n);

 

5. printf

8051 does not have its own UART interface, and needs to use SP7021's UART0--UART4 to realize debug output.

Because IOP reads and writes memory through RBUS, the efficiency is slow, so the printf of 8051 relies on the printf of arm926 to achieve.

#define DEBUG_ENABLE Can be used to turn on/off printf output.

By default, the status information is passed through IOP_DATA8_L, and the data is passed through IOP_DATA8_H.

Printf flow :

The putchar function is implemented to load data into the 128-byte fifo buffer, and write a piece of data to IOP_DATA8_H, then set IOP_DATA8_L to 0x03. Timely check whether the second bit of IOP_DATA8_L is 0 through timer0. If it is detected that the second bit is 0 and there is data in the fifo buffer, then take the next data and write it to IOP_DATA8_H, and set the second bit of IOP_DATA8_L to 1 at the same time. Loop detection until the data in fifo is cleared. (printf uses timer0, so if other functions require a timer, you can use timer1 or turn off the DEBUG_ENABLE configuration).

 

And arm926 will always poll whether IOP_DATA8_L is 0x03, if it is 0x03, then take out 1 data from IOP_DATA8_H and output it to the terminal through printf, and clear the 2nd bit of IOP_DATA8_L, and keep looping until IOP_DATA8_L is 0x0, indicating that there is no fifo in IOP_DATA8_L Data needs to be exported. So the debug output terminal of IOP is configured by the nonos code run by arm926.

Arm926 polling IOP_DATA8 and printf data.

6. I2C slave

I2C implements the basic protocol of i2c slave, data receiving and sending, and data communication with RISC.

The I2C slave can support a communication frequency of up to 100k.

 

i2c slave flow:

Set the I2C_SLAVE_CTL register before IOP work, including i2c enable signal and i2c slave address.

The IOP main loop function detects that bit7 of I2C_SLAVE_CTL is 1, then enters I2C_Slave mode, enters i2c_slave_init to initialize i2c, and obtains the i2c slave address from I2C_SLAVE_CTL.

Then enter the while loop and always detect the i2c start signal. If the i2c start signal is detected, enter the i2c_slave_protocol function to process the i2c protocol. The data received and the data to be sent during the i2c communication process are all from the 32byte RX/TX fifo Obtain.

 

If the i2c start signal is not detected in the while loop, enter i2c_slave_save_data/i2c_slave_get_data, load the RISC data into the TX fifo and write the data accepted by the I2C from the RX fifo to the RISC. (The data transmission method is the same as printf, the data is transmitted through one IOP_DATA, and the other IOP_DATA saves the transmission status), and at the same time, check whether the bit7 of I2C_SLAVE_CTL (IOP_DATA0_L) is 0, if it is 0, exit the i2c slave mode.

The SCL and SDA 2 pins of I2C are controlled by gpio, so you need to follow the GPIO control method. Before entering the IOP, set the SCL and SDA pins in the RISC to the GPIO port mode and the IOP control method.

 

If you need to modify the communication register between RISC and IOP, you can modify it in include/drivers/i2c/i2c_slave.h