Friday, March 14, 2014

STM32F429I Discovery SMS Emulator on Linux

1 Introduction

Alessandro Rocchegiani released some interesting projects on STM32 series MCUs. This article introduces the adding support to be built and burned under Linux, with the project STM32F429 Discovery Sega Master System Emulator as an example.

2 ARM Toolchain, Cross Compiler

If the host is a Debian or Ubuntu distribution, the GNU ARM toolchain package is available. To install it:
% sudo apt-get install gcc-arm-none-eabi
Or (option) please download the prebuilt package, unpack it to wherever you like, and configure the path accordingly.
% export PATH=$HOME/ARM-toolchain/gcc-arm-none-eabi-4_8-2013q4/bin;$PATH
Alternately (option) you can also use toolchain that is preferred: Mentor Graphics Sourcery CodeBench , DENX ELDK.
(option) Hackers like you may also love the funniest way, to build your own toolchain:

3 Flash Tools

We are going to use either stlink or openocd to burn our images to STM32F4 flash. Actually they are not only just flash programming utilities, but also can be used as on-chip debuggers. This article covers the flash programming usage.

3.1 stlink

To install stlink utility.
% git clone https://github.com/texane/stlink.git
% cd stlink
% ./autogen.sh
% ./configure
% make
% sudo make install
To write the file (hex or bin for example) to a specific address of flash.
% st-flash write <file path> <addr>
GUI version is also available (GTK+ development library required). It works similarily to ST Utility.
% ./configure --with-gtk
% make
% sudo make install
% stlink-gui
Connect the Discovery board and you are able to choose file to flash to some address.

3.2 OpenOCD (option)

Install the OpenOCD either with APT.
% sudo apt-get install openocd
Or (recommented) building from the latest GIT version:
% git clone git://git.code.sf.net/p/openocd/code openocd
% cd openocd
% ./bootstrap
% ./configure --prefix=/usr/local --enable-stlink
% make
% sudo make install
Load the correct script files.
% openocd -f interface/stlink-v2.cfg -f board/stm32f429discovery.cfg
Open On-Chip Debugger 0.8.0-dev-00350-g6c74255 (2014-02-21-16:59)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.sourceforge.net/doc/doxygen/bugs.html
Warn : Interface already configured, ignoring
Error: already specified hl_layout stlink
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : This adapter doesn't support configurable speed
Info : STLINK v2 JTAG v17 API v2 SWIM v0 VID 0x0483 PID 0x3748
Info : using stlink api v2
Info : Target voltage: 2.864019
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
Then on another terminal, telnet to port 4444, and enter commands in the interactive prompt.
% telnet localhost 4444
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
Open On-Chip Debugger
> init
> reset init
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x08005b54 msp: 0x20030000
> flash write_image erase /home/winfred/STM32F429I-Disco_SMS_EMU/smsemu.elf
auto erase enabled
target state: halted
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x20000042 msp: 0x20030000
wrote 49152 bytes from file /home/winfred/STM32F429I-Disco_SMS_EMU/smsemu.elf in 2.317470s (20.712 KiB/s)
> reset run
You can also list down the openocd commands in order and execute them in one shot.
% openocd -f interface/stlink-v2.cfg -f board/stm32f429discovery.cfg -c "init" -c "reset init" -c "flash write_image erase smsemu.elf" -c "reset run" -c "exit"

4 Build the project

Download the project to the same directory of the STM32F429I discovery firmware package.
% ls
STM32F429I-Disco_SMS_EMU/  STM32F429I-Discovery_FW_V1.0.1/
% cd STM32F429-Disco_SMS_EMU
Modify the main.c:Cartridge according to your ROMs then make to build and program the flash.
% make
% make flash
% make flash-rom
Then you are good to play.

Click to choose a ROM. Press the user button to start.

5 Code Snippets

5.1 Makefile

Cortex-M4 implements the ARMv7E-M architecture and it supports Thumb mode only.
CFLAGS = -mcpu=cortex-m4 -march=armv7e-m -mtune=cortex-m4
CFLAGS += -mlittle-endian -mthumb

5.2 startup_stm32f429_439xx.s

Copy the startup file from STM32F429I discovery firmware package and mark the Cortex M3 and VFP out.
% diff -w -u ../STM32F429I-Discovery_FW_V1.0.1/Libraries/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc_ride7/startup_stm32f429_439xx.s startup_stm32f429_439xx.S
--- ../STM32F429I-Discovery_FW_V1.0.1/Libraries/CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc_ride7/startup_stm32f429_439xx.s      2013-11-11 16:21:22.000000000 +0800
+++ startup_stm32f429_439xx.S   2014-03-11 21:24:10.321587756 +0800
@@ -37,1 +37,1 @@
   */

   .syntax unified
-  .cpu cortex-m3
-  .fpu softvfp
+  /*.cpu cortex-m3
+  .fpu softvfp*/
   .thumb

 .global  g_pfnVectors

5.3 CPU_gcc.c

Besides converting CPU_exec() to GCC inline assembly, another option would be to create an assembly file defining the CPU_exec() function able to be called from C. Both work for me. Here the provided source code goes with the iline assembly option.
Modifications are done to be understood and compiled by GCC, including adding IT blocks for Thumb conditional instructions.
diff --git a/CPU_gcc.c b/CPU_gcc.c
index af62852..54ea83f 100644
--- a/CPU_gcc.c
+++ b/CPU_gcc.c
@@ -230,6 +230,7 @@ int32_t CPU_exec(int32_t cycle,void* cpu)
 "   TST   r_acc,#0x80              \n" // CPU Halt State
 "   BEQ   cpu_run \n"
 "   ANDS  r_acc,r_cyc,#3           \n" // se((cyc & 3)!=0)
+"   IT    ne \n"
 "   SUBNE r_acc,#4                 \n" // cyc-=4
 "   POP   {r1-r12,pc}              \n" // exit
 " cpu_run: \n"
@@ -543,6 +544,7 @@ int32_t CPU_exec(int32_t cycle,void* cpu)
 "   MOVT  r_pc,#0 \n"
 "   LSR   r_res,r_op,#3 \n"
 "   CMP   r_res,#8                 \n" //opcode >= 0x40 ?
+"   ITT   ge \n"
 "   LSRGE r_res,#3 \n"
 "   ORRGE r_res,#8                 \n" //res=(opcode/8)|8
 "   AND   r_acc,r_op,#7 \n"
 ...
syntax adjustments such as not to omit Rd in instructions with logical shift immediate,
diff --git a/CPU_gcc.c b/CPU_gcc.c
index 9fdcafe..d0a474b 100644
--- a/CPU_gcc.c
+++ b/CPU_gcc.c
@@ -1076,5 +1076,5 @@ int32_t CPU_exec(int32_t cycle,void* cpu)
 "   LDRB  r_pnt,[r_pnt,r_pc]       \n"
 "   ADD   r_pc,#1 \n"
 "   MOVT  r_pc,#0 \n"
-"   ORR   r_ad,r_pnt,lsl#8 \n"
+"   ORR   r_ad,r_ad,r_pnt,lsl#8 \n"
 "   BX    lr \n"

 // imposta r_acc con (r_ad) e (r_ad+1)
defining allocating datas in GAS style,
.byte  ((MemReg0 - MempntSwitch)/2)
.byte  ((MemReg1 - MempntSwitch)/2)
.byte  ((MemReg2 - MempntSwitch)/2)
Register renaming and constant defining in GAS style.
r_acc .req r0
r_jmp .req r1
r_ad  .req r2
...
.equ  ofsBC,   0x00
.equ  ofsB,    0x00
.equ  ofsC,    0x01
...
and to correct the label syntax, etc.
diff --git a/CPU_gcc.c b/CPU_gcc.c
index d74d679..b1058bb 100644
--- a/CPU_gcc.c
+++ b/CPU_gcc.c
@@ -588,7 +588,8 @@ int32_t CPU_exec(int32_t cycle,void* cpu)
 "   .hword  ((PAD_CB    - CB_Prefix_Opcode)/2)  \n" //
 "   .hword  ((PAD_CB    - CB_Prefix_Opcode)/2)  \n" //

-" PAD_CB  BX lr                    \n" //(DEBUG Safe)
+" PAD_CB: \n"
+"   BX lr                    \n" //(DEBUG Safe)

 //***********
 // xD Prefix
Date: 2014-03-14 Fri
Author: Winfred Lu
Created: 2014-03-15 Sat 09:32
Emacs 24.3.50.1 (Org mode 8.2.4)

4 comments:

Vinod.S said...

vinod@vinod:~/STM32F429I-Disco_SMS_EMU$ make
arm-none-eabi-gcc -mcpu=cortex-m4 -march=armv7e-m -mtune=cortex-m4 -mlittle-endian -mthumb -g -std=c99 -Wall -g -std=c99 -O3 -ffast-math -ffunction-sections -fdata-sections -Wl,--gc-sections -fno-common --param max-inline-insns-single=1000 -DSTM32F429_439xx -DVECT_TAB_FLASH -I. -I../STM32F429I-Discovery_FW_V1.0.1/Libraries/CMSIS/Device/ST/STM32F4xx/Include -I../STM32F429I-Discovery_FW_V1.0.1/Libraries/CMSIS/Include -DUSE_STDPERIPH_DRIVER -I../STM32F429I-Discovery_FW_V1.0.1/Libraries/STM32F4xx_StdPeriph_Driver/inc -I../STM32F429I-Discovery_FW_V1.0.1/Utilities/STM32F429I-Discovery -c main.c -o main.o
main.c:16:23: fatal error: stm32f4xx.h: No such file or directory
#include "stm32f4xx.h"
^
compilation terminated.
make: *** [main.o] Error 1

Winfred said...

Hi Vinod,

Please download the STM32F429 discovery firmware package and put it under the same folder of the SMS emulator project.

Vinod.S said...

Hi Winfred,
THanks, Now I can build and flash the binary. Also I can see the game selection menu in screen. But I couldn't find the cartridge rom in the folder, so the make flash-rom gives error..

vinod@vinod:~/STM32F429I-Disco_SMS_EMU$ make flash-rom
st-flash write ./SMS-ROM/"Alex Kidd in Miracle World.bin" 0x8080000
2014-07-27T16:30:08 INFO src/stlink-common.c: Loading device parameters....
2014-07-27T16:30:08 INFO src/stlink-common.c: Device connected is: F42x and F43x device, id 0x10036419
2014-07-27T16:30:08 INFO src/stlink-common.c: SRAM size: 0x30000 bytes (192 KiB), Flash: 0x200000 bytes (2048 KiB) in pages of 16384 bytes
open(./SMS-ROM/Alex Kidd in Miracle World.bin) == -1
2014-07-27T16:30:08 ERROR src/stlink-common.c: map_file() == -1
stlink_fwrite_flash() == -1
make: *** [flash-rom] Error 255
vinod@vinod:~/STM32F429I-Disco_SMS_EMU$

Winfred said...

Hi Vinod,
The source code does't include any ROMs. Please try to download whatever you like from the internet.

FreeROMS
CoolROM