Tuesday, July 3, 2012

things about STM32L-discovery

這邊筆記跟 @jserv 前輩學習 STM32 小玩具的一些小心得

暑假前應該這篇文章會一直更新(?)

============================================

首先準備 tool chains

到這裡去找 arm-none-eabi 的工具

http://www.mentor.com/embedded-software/sourcery-tools/sourcery-codebench/editions/lite-edition/request?id=e023fac2-e611-476b-a702-90eabb2aeca8&returnURL=https%3A%2F%2Fsourcery%2Ementor%2Ecom%2FGNUToolchain%2Frelease830%3Flite%3Darm%26cmpid%3D7108%26downloadlite%3Dscblite2012&downloadlite=scblite2012&fmpath=/embedded-software/sourcery-tools/sourcery-codebench/editions/lite-edition/form

很該死的註冊完後應該可以抓到  arm-2012.03-56-arm-none-eabi-i686-pc-linux-gnu.tar.bz2

解壓後放在一個跟 project 相關的資料夾之後把路徑加到現在的 bash shell 上

export PATH=$PATH:/home/xatierlike/stlink/arm-2012.03/bin

然後到 github.com 上面去把   texane/stlink  clone 下來


git clone https://github.com/texane/stlink.git


然後想辦法去找個可以玩耍的 demo code 下來...

昨天跟 jserv 找可以用的 code 找到快哭了QQ 幾乎沒一個能用的 OTZ

最後找到這邊去把他的 blinky.zip 弄下來

http://gostm32.blogspot.tw/2010/09/blinky-ii.html

這裡還需要用 vim 改一下 Makefile,%s/elf/none-eabi/g

原本的 blinky 是用  arm-elf-* tools 工作的,而我用的是 arm-none-eabi-* tools

會後把 make 出來的 blinky.bin 燒進去 STM32 的 flash 就可以囉

./st-flash write ../blinky/blinky.bin 0x8000000

./st-util 搭配 gdb target remote 可以拿來 debug ,不過我還沒玩熟

============================================

又研究了一下下,成功用 gdb 跟小板子玩耍了 XD

vim Makefile

CFLAGS  = -I./ -c -fno-common -Os -mcpu=cortex-m3 -mthumb -g

打開 compiler 的  debug flag

make clean; make

重新燒進去

./st-flash write ../blinky/blinky.bin 0x8000000

聽一下 :4242 port

./st-util

arm-none-eabi-gdb blinky.none-eabi

(gdb) target remote :4242
Remote debugging using :4242
main () at blinky.c:37
37        RCC->APB2ENR |= 0x10 | 0x04;                                    /* Enable the GPIOA (bit 2) and GPIOC (bit 8) */
(gdb) n
33    int main(void){    
(gdb) n
37        RCC->APB2ENR |= 0x10 | 0x04;                                    /* Enable the GPIOA (bit 2) and GPIOC (bit 8) */
(gdb) list
32    
33    int main(void){    
34        int n = 0;
35        int button;
36    
37        RCC->APB2ENR |= 0x10 | 0x04;                                    /* Enable the GPIOA (bit 2) and GPIOC (bit 8) */
38        GPIOC->CRH = 0x11;                                                /* Set GPIOC Pin 8 and Pin 9 to outputs */
39        GPIOA->CRL = 0x04;                                                /* Set GPIOA Pin 0 to input floating */
40    
41        while(1)
(gdb)

可以開始想辦法看懂 code 惹QQ


============================================

今天晚上跟 jserv 研究他那邊弄過來的 qemu code

不知道為什麼我這邊就是跑不起來 ><

於是先去吃宵夜了XD

吃完宵夜後回來改 code 把中間改掉... 神奇的可以動了

serversocket.bind((socket.gethostname(), port)) 

serversocket.bind(("localhost", port))



但是當下我不知道為什麼,明明 socket.gethostname 得到的也是我機器的 hostname ...

根據 http://docs.python.org/library/socket.html#socket.gethostname


Return a string containing the hostname of the machine where the Python interpreter is currently executing.

我在我本機工作, gethostname 要到的理論上是跟在 shell 上打 hostname 一樣的結果

後來 方括號拉拉學長 提出了令我念頭一轉的問題點

問題是你hostname如果和我一樣在/etc/hosts查不到,你就不能直接用"localhost"去bind, 要用127.0.0.1

我馬上 ...

cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 xatierlike-NB


幹~~~~~~ 單純我 /etc/hosts 不知道哪時候被我腦殘動到 OTZ

============================================

原本聽 jserv 的去找 github 上面的 Qemu git

不斷的 make 失敗...

結果後來才發現東西是太舊的..囧

真。Qemu git

http://git.qemu.org/qemu.git



arm instruction
it instruction, condition (cbz / cnbz)



============================================
basic computer organization
http://goo.gl/jW6A0

Intro to Git for the Perl Hacker
http://marklodato.github.com/visual-git-guide/index-en.html


git labs
http://gitimmersion-apputu.rhcloud.com/
http://gitimmersion-apputu.rhcloud.com/lab_42.html




arn instructions
http://simplemachines.it/doc/arm_inst.pdf


arm-none-eabi-gcc -static -fno-common -Os -mcpu=cortex-a9 -mthumb test.c
arm-none-eabi-objdump -d a.out

git rebase => http://codepad.org/hehastWp

2252  git diff 
 2253  cd ..
 2254  ls
 2255  git diff 
 2256  git diff  > p.diff
 2257  git reset --hard HEAD
 2258  git log
 2259  git branch 
 2260  git branch summer-work
 2261  git checkout summer-work 
 2262  tig
 2263  sudo apt-get install tig
 2264  tig
 2265  git apply p.diff
 2266  git diff
 2267  git commit -a
 2268  export EDITOR=vim
 2269  git commit -a
 2270  git commit --amend 
 2271  vi README 
 2272  git add README
 2273  git commit --amend 
 2274  tig
 2275  git format-patch -1
 2276  vi 0001-Attempt-to-build-STM32-related-files.patch 
 2277  git rebase --help
 2278  git branch -r
 2279  git fetch origin 
 2280  tig
 2281  git log
 2282  git log origin/master 
 2283  git rebase origin/master 
 2284  tig
 2285  git checkout master 
 2286  git pull

Harvard vs Von Nuemann Architecture
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka11516.html
http://www.pictutorials.com/Harvard_vs_Von_Nuemann_Architecture.htm
http://embeddedbasics.wordpress.com/2009/12/18/von_neumann_architecture_vs_harvard_architecture/

http://89c51.blogspot.tw/2007/11/von-neumann-architecture-and-harvard.html

Harvard architecture impossible to self-modified code (data:RW, code:RO)


ijsung: 不過現在的harvard architecture只是說指令跟資料有各自的L1 cache,出了L1之後還是共用的。要self-modified code是可行的,只是要flush L1 caches
好比說ARM7是Princeton arch, 到了ARM9是Harvard arch, 但是仍然可以self-modify只是多了一步flush L1 I/D cache


way Harvard arch 適合 DSP  =>資料大小的關係

ijsung: 
因為同一個clock cycle內可以同時從instruction cache跟data cache各抓出指令跟資料
DSP的working set通常遠大於register file能裝的大小,所以能夠每個clock cycle都能從data memory抓東西進來的harvard arch樂勝。
大部分的DSP都有玄妙的指令集可以在同一個cycle內1)抓下個指令所需的資料 2)用上個週期抓進來的資料計算
若是Princeton arch的話這是不可能的。要抓data的話instruction fetch就得暫停。

http://sestevenson.wordpress.com/2009/10/01/implementation-of-fir-filtering-in-c-part-1/



qemu-devel mailling list archive
http://lists.gnu.org/archive/html/qemu-devel/



大概猜出 make 失敗的原因,cpu_register_* 這組 API 好像有被換過了

docs/memory 裏面有寫



MMIO Operations                                                              
---------------                                                              
                                                                             
MMIO regions are provided with ->read() and ->write() callbacks; in addition  
various constraints can be supplied to control how these callbacks are called:
                                                                             
 - .valid.min_access_size, .valid.max_access_size define the access sizes    
   (in bytes) which the device accepts; accesses outside this range will      
   have device and bus specific behaviour (ignored, or machine check)        
 - .valid.aligned specifies that the device only accepts naturally aligned    
   accesses.  Unaligned accesses invoke device and bus specific behaviour.    
 - .impl.min_access_size, .impl.max_access_size define the access sizes      
   (in bytes) supported by the *implementation*; other access sizes will be  
   emulated using the ones available.  For example a 4-byte write will be    
   emulated using four 1-byte writes, if .impl.max_access_size = 1.          
 - .impl.valid specifies that the *implementation* only supports unaligned    
   accesses; unaligned accesses will be emulated by two aligned accesses.    
 - .old_portio and .old_mmio can be used to ease porting from code using      
   cpu_register_io_memory() and register_ioport().  They should not be used  
   in new code.  


git blame -L 160,170 docs/memory.txt 看一下,應該是這個 commit 有動到(? 明天研究

9d3a4736 (Avi Kivity

2011-07-26 14:26:00 +0300 170 差不多是去年這個時候 lol

7/26 早上跟 jserv 整理了一下大概有動到的東西

SysBusDevieceInfo
memory regi
cpu_register_io_memory
qdev_init_chardev
macro

目前 led / button 好像修好了,來嘗試 GPIO 的 porting



qemu/docs/tracing.txts

http://www.pcmech.com/forum/computer-hardware/153070-guide-dual-core-smp.html
L2 cache, SMP dual core
Symmetric Multi-Processing (SMP)
mmio
interrupt VS trap system call
I2C / I2S


============================================
8/7 update


gdb
run <參數>  # run the program
bt          # backtrace
c           # continue
list


# read a elf file
readelf -a main_qemu.elf  | less


# dynamic link program needs a interpreter
/lib/ld-linux.so.2      # interpreter


# 飛上天的方法 (平行化處理 make)
make -j2


# 思考
can dynamic linker run alone?


# 蹦床,很多關於 stack 的操過跟這個有關
http://en.wikipedia.org/wiki/Trampoline_(computing)



.eh_frame_hdr



# 本周要完成的進度
修好 UI

refactoring

產生 format-patch



# 相關的 git commands
git git format-patch -1

git am 0001-try-to-fix-core-dump.patch -s

git stash
git rebase
git blame
git apply



============================================
8/8 update


今日主要修改,猜出(?) momory region 需要的大小

hw/stm32.c 最上面

#define GPIO_A 0 //0x4002 0000 - 0x4002 03FF                                    
#define GPIO_B 1 //0x4002 0400 - 0x4002 07FF                                    
#define GPIO_C 2 //0x4002 0800 - 0x4002 0BFF                                    
#define GPIO_D 3 //0x4002 0C00 - 0x4002 0FFF                                    
#define GPIO_E 4 //0x4002 1000 - 0x4002 13FF                                    
#define GPIO_H 5 //0x4002 1400 - 0x4002 17FF


就猜他應該會用 0x17FF 這麼多,於是把 hw/stm32_gpio.c 裏面上次卡關的 size 換掉

memory_region_init_io(&s->iomem, &gpio_ops, s, "gpio", 0x17FF);

另外 LED 也找出來應該是用錯 API 了

-    s->chr = qemu_char_get_next_serial();

+    s->chr = qemu_chr_find(id);

換掉後就成功跟 UI 連結了,看到燈在閃的那瞬間感動的快哭出來了 QQ

順手重購了一下,法國人寫的 code 有點亂 :3

至於 assert 造成 core dump 的原因還要再找找


============================================
8/10 update

實在是太開心了


這兩天自己研究了很久還是沒想出解法,gdb 挖了半天沒有想法

去找了一些相關於 stellaris 的 mailing list,也是很多人有遇到相關的議題

http://lists.gnu.org/archive/html/qemu-devel/2012-07/msg03040.html

https://bugs.launchpad.net/qemu/+bug/1028260

最後找到這個 patch

http://patchwork.ozlabs.org/patch/172820/

apply 後好像就能動了,耶~


============================================
8/15 update

整理完 code,fetch 一下 master 

順便 fetch 一下 qemu 的 master 打算來製作 format patch

上次那個找到的 fix coredump 的 patch 在昨天下午有被到 master 

感謝 Anthony Liguori <aliguori@us.ibm.com> !



我嘗試 rebase 後就 Makefile 噴衝突了,感覺像是有人把架構又切開了

(這部份還要追 log)

手動 merge (不知道有沒有出問題XD)


目前先另外開一個 branch 製作 patch 

底下是我用 git format-patch -2 生出來的 patch





From 808d1135a501fe0c524c7572fd82ca904d1d5900 Mon Sep 17 00:00:00 2001
From: xatier <xatierlike@gmail.com>
Date: Tue, 14 Aug 2012 14:37:46 +0800
Subject: [PATCH 1/2]     try to support stm32l152rbt6 board

---
 hw/arm/Makefile.objs |    1 +
 hw/stm32.c           |  189 ++++++++++++++++++++++++++++
 hw/stm32_gpio.c      |  331 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 521 insertions(+)
 create mode 100644 hw/stm32.c
 create mode 100644 hw/stm32_gpio.c

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 2b39fb3..15aff03 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -9,6 +9,7 @@ obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
 obj-y += exynos4210_rtc.o exynos4210_i2c.o
 obj-y += arm_mptimer.o a15mpcore.o
 obj-y += armv7m.o armv7m_nvic.o stellaris.o stellaris_enet.o
+obj-y += stm32.o stm32_gpio.o 
 obj-y += highbank.o
 obj-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
 obj-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
diff --git a/hw/stm32.c b/hw/stm32.c
new file mode 100644
index 0000000..a57d384
--- /dev/null
+++ b/hw/stm32.c
@@ -0,0 +1,189 @@
+
+#include "sysbus.h"
+#include "exec-memory.h"
+#include "arm-misc.h"
+#include "boards.h"
+#include "devices.h"
+
+#define NB_NVIC_IRQ 5
+#define NB_GPIO 6
+
+#define GPIO_A 0             // 0x4002 0000 - 0x4002 03FF
+#define GPIO_B 1             // 0x4002 0400 - 0x4002 07FF
+#define GPIO_C 2             // 0x4002 0800 - 0x4002 0BFF
+#define GPIO_D 3             // 0x4002 0C00 - 0x4002 0FFF
+#define GPIO_E 4             // 0x4002 1000 - 0x4002 13FF
+#define GPIO_H 5             // 0x4002 1400 - 0x4002 17FF
+
+typedef const struct { 
+    const char *name;
+    uint16_t  f_size;
+} stm32_board_info;
+
+
+
+static stm32_board_info stm32_board = {
+    "stm32l152rbt6",
+    0x0080,                 // 128kb flash
+};
+
+
+
+typedef struct {
+    uint32_t int_status;
+    uint32_t int_mask;
+    MemoryRegion iomem;
+    qemu_irq irq;
+    stm32_board_info* board;
+} ssys_state;
+
+
+
+static void ssys_reset(void *opaque) {
+    // unimplemtmented
+}
+
+
+
+static uint64_t ssys_read(void *opaque, target_phys_addr_t offset, 
+                                                    unsigned size)
+{
+    // nothing to do
+    return 0;
+}
+
+
+
+static void ssys_write(void *opaque, target_phys_addr_t offset, uint64_t value,
+                                                                unsigned size)
+{
+    // unimplemtmented
+}
+
+
+
+static const MemoryRegionOps ssys_ops = {
+    .read  = ssys_read,
+    .write = ssys_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+
+
+static int stm32_sys_post_load(void *opaque, int version_id)
+{
+    //Nothing to do
+    return 0;
+}
+
+
+
+static const VMStateDescription vmstate_stm32_sys = {
+    .name               = "stm32_sys",
+    .version_id         = 2,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .post_load          = stm32_sys_post_load,
+    .fields             = (VMStateField[]) {
+        VMSTATE_UINT32(int_mask, ssys_state),
+        VMSTATE_UINT32(int_status, ssys_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+
+static int stm32l_sys_init(uint32_t base, qemu_irq irq,
+        stm32_board_info * board)
+{
+    ssys_state *s;
+
+    s = (ssys_state *)g_malloc0(sizeof(ssys_state));
+    s->irq   = irq;
+    s->board = board;
+
+    memory_region_init_io(&s->iomem, &ssys_ops, s, "ssys", 0x00010000);
+    memory_region_add_subregion(get_system_memory(), base, &s->iomem);
+
+    ssys_reset(s);
+    vmstate_register(NULL, -1, &vmstate_stm32_sys, s);
+
+    return 0;
+}
+
+
+
+static void stm32l152rbt6_init(ram_addr_t ram_size, const char *boot_device,
+        const char *kernel_filename, const char *kernel_cmdline, 
+        const char *initrd_filename, const char *cpu_model)
+{
+
+    // prepare the memory
+    MemoryRegion *address_space_mem = get_system_memory();
+    uint16_t flash_size = stm32_board.f_size;   // 128KBits
+    uint16_t sram_size = 0x0010;                // 16 KBits
+    
+    
+    // initial the processor  (+ memory)
+    qemu_irq* pic = armv7m_init(address_space_mem, flash_size, sram_size,
+                                            kernel_filename, cpu_model);
+    stm32l_sys_init(0x1FF00000, pic[28], &stm32_board);
+    
+    // structures GPIO
+    static const uint32_t gpio_addr[NB_GPIO] = {
+        0x40020000,             // GPIO_A
+        0x40020400,             // GPIO_B
+        0x40020800,             // GPIO_C
+        0x40020C00,             // GPIO_D
+        0x40021000,             // GPIO_E
+        0x40021400              // GPIO_H
+    };
+
+    DeviceState* gpio_dev[NB_GPIO];    
+    
+    //create the botton
+    DeviceState* button = sysbus_create_simple("stm32_button", -1, NULL);
+    
+    //create the LEDs
+    DeviceState* led_dev6 = sysbus_create_simple("stm32_led_blue" , -1, NULL);
+    DeviceState* led_dev7 = sysbus_create_simple("stm32_led_green", -1, NULL);
+    
+    
+    //initial GPIO_A
+    gpio_dev[GPIO_A] = sysbus_create_varargs("stm32_gpio_A", gpio_addr[GPIO_A],
+                                                                        NULL);
+    qemu_irq entreeBouton = qdev_get_gpio_in(gpio_dev[GPIO_A], 1);
+
+    qdev_connect_gpio_out(button, 0, entreeBouton);
+    
+    //initial GPIO_B
+    gpio_dev[GPIO_B] = sysbus_create_varargs("stm32_gpio_B", gpio_addr[GPIO_B],
+                                                                        NULL);
+
+    qemu_irq entreeLED6 = qdev_get_gpio_in(led_dev6, 0);
+    qdev_connect_gpio_out(gpio_dev[GPIO_B], 6, entreeLED6);
+    qemu_irq entreeLED7 = qdev_get_gpio_in(led_dev7, 0);
+    qdev_connect_gpio_out(gpio_dev[GPIO_B], 7, entreeLED7);
+    
+}
+
+static QEMUMachine stm32l152rbt6_machine = {
+    .name = "stm32l152rbt6",
+    .desc = "STM32L Discovery",
+    .init = stm32l152rbt6_init,
+    // user manual page 105
+    // STM32 = ARM-based 32-bit microcontroller
+    // L = Low power
+    // 152: Devices with LCD
+    // R = 64 pins
+    // B = 128 Kbytes of Flash memory
+    // T = LQFP
+    // 6 = Industrial temperature range, –40 to 85 °C
+};
+
+static void stm32l_machine_init(void) {
+    qemu_register_machine(&stm32l152rbt6_machine);
+}
+
+machine_init(stm32l_machine_init);
+
diff --git a/hw/stm32_gpio.c b/hw/stm32_gpio.c
new file mode 100644
index 0000000..0ac3535
--- /dev/null
+++ b/hw/stm32_gpio.c
@@ -0,0 +1,331 @@
+#include "sysbus.h"
+#include "exec-memory.h"
+
+#define NB_PIN 64
+
+typedef struct {
+    SysBusDevice busdev;
+
+    /* Registres GPIO (Reference Manual p119 */
+    uint32_t mode; /* Mode */
+    uint16_t otype; /* Output type */
+    uint32_t ospeed; /* Output speed */
+    uint32_t pupd; /* Pull-up/Pull-down */
+    uint16_t ind; /* Input data */
+    uint16_t outd; /* Output data register */
+    uint16_t outd_old; /* Output data register */
+    uint32_t bsr; /* Bit set/reset */
+    uint32_t lck; /* Lock */
+    uint32_t afrl; /* Alternate function low */
+    uint32_t afrh; /* Alternate function high */
+    
+    qemu_irq irq_out[NB_PIN];
+    unsigned char id;
+    MemoryRegion iomem;
+} stm32_gpio_state;
+
+
+static const VMStateDescription vmstate_stm32_gpio = {
+    .name       = "stm32_gpio",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[])
+    {
+        VMSTATE_UINT32(mode, stm32_gpio_state),
+        VMSTATE_UINT16(otype, stm32_gpio_state),
+        VMSTATE_UINT32(ospeed, stm32_gpio_state),
+        VMSTATE_UINT32(pupd, stm32_gpio_state),
+        VMSTATE_UINT16(ind, stm32_gpio_state),
+        VMSTATE_UINT16(outd, stm32_gpio_state),
+        VMSTATE_UINT16(outd_old, stm32_gpio_state),
+        VMSTATE_UINT32(bsr, stm32_gpio_state),
+        VMSTATE_UINT32(lck, stm32_gpio_state),
+        VMSTATE_UINT32(afrl, stm32_gpio_state),
+        VMSTATE_UINT32(afrh, stm32_gpio_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+static void stm32_gpio_update(stm32_gpio_state *s)
+{
+    uint16_t changed;
+    uint16_t mask;
+    int i;
+    
+    changed = s->outd_old ^ s->outd; //XOR: Tous les bits à 1 seront les bits changés
+    if (!changed)
+        return;
+
+    s->outd_old = s->outd;
+    for (i = 0; i < 8; i++) {
+        mask = 1 << i;
+        if (changed & mask) {
+            //Conditions de changement
+            //--Mode register != 00
+            uint32_t modeMask = (1 << (i*2)) | (1 << ((i*2) + 1));
+            if((s->mode & modeMask) != 0) {
+                // send an IRQ to the device connected to the GPIO pin
+                //FIXME: the transmition to the pin is direct when it should occur at the next tick of the RCC
+                qemu_set_irq(s->irq_out[i], (s->outd & mask) != 0);
+            }
+        }
+    }
+}
+
+
+static uint64_t stm32_gpio_read(void *opaque, target_phys_addr_t offset, unsigned size)
+{
+    stm32_gpio_state *s = (stm32_gpio_state *) opaque;
+
+    switch (offset) {
+        case 0x00: /* Mode */
+            return s->mode;
+        case 0x04: /* oType */
+            return s->otype;
+        case 0x08: /* oSpeed */
+            return s->ospeed;
+        case 0x0C: /* Pull-up / Pull-down */
+            return s->pupd;
+        case 0x10: /* Input data register */
+            return s->ind;
+        case 0x14: /* Output data */
+            return s->outd;
+        case 0x18: /* BSR */
+            return 0x0; //Write only
+        case 0x1C: /* lock */
+            return 0x0; //Non implémenté
+        case 0x20: /* AFRL */
+            return s->afrl;
+        case 0x24: /* AFRH */
+            return s->afrh;
+        default:
+            hw_error("stm32_gpio_read: Bad offset %x\n", (int) offset);
+            return 0;
+    }
+}
+
+
+static void stm32_gpio_write(void *opaque, target_phys_addr_t offset, 
+                                        uint64_t value, unsigned size)
+{
+    stm32_gpio_state *s = (stm32_gpio_state *) opaque;
+    int i;
+    uint16_t low = (uint16_t)value;
+    uint16_t high = (uint16_t)value >> 16;
+    
+    switch (offset) {
+        case 0x00: /* Mode */
+            s->mode = value;
+            break;
+        case 0x04: /* oType */
+            s->otype = value;
+            break;
+        case 0x08: /* oSpeed */
+            s->ospeed = value;
+            break;
+        case 0x0C: /* Pull-up / Pull-down */
+            s->pupd = value;
+            break;
+        case 0x10: /* Input data register */
+            //Read only
+            break;
+        case 0x14: /* Output data */
+            s->outd = value;
+            stm32_gpio_update(s);
+            break;
+            
+        case 0x18: /* BSR */
+            //set = low
+            //reset = high
+            for(i=0; i<16; i++) {
+                int mask = 1 << i;
+                if((high & mask) != 0) { //Si bit reset[i]
+                    s->outd &= ~(1 << i); //Mise à 0
+                }
+                if((low & mask) != 0) { //Si bit reset[i]
+                    s->outd |= (1 << i); //Mise à 1
+                }
+            }
+            stm32_gpio_update(s);
+            break;
+            
+        case 0x1C: /* lock */
+            //FIXME: Non implémenté
+            break;
+        case 0x20: /* AFRL */
+            s->afrl = value;
+            break;
+        case 0x24: /* AFRH */
+            s->afrh = value;
+            break;
+        default:
+            hw_error("stm32_gpio_write: Bad offset %x\n", (int) offset);
+    }
+}
+
+
+
+static void stm32_gpio_reset(stm32_gpio_state *s)
+{
+    switch (s->id) {
+        case 'A':
+            s->mode = 0xA8000000;
+            s->pupd = 0x64000000;
+            s->ospeed = 0x00000000;
+            break;
+        case 'B':
+            s->mode = 0x00000280;
+            s->pupd = 0x00000100;
+            s->ospeed = 0x000000C0;
+            break;
+        default:
+            s->mode = 0x00000000;
+            s->pupd = 0x00000000;
+            s->ospeed = 0x00000000;
+    }
+    
+    //Valeur commune
+    s->ind =    0x00000000;
+    s->otype =  0x00000000;
+    s->outd =   0x00000000;
+    s->outd_old = s->outd;
+    s->bsr =    0x00000000;
+    s->lck =    0x00000000;
+    s->afrh =   0x00000000;
+    s->afrl =   0x00000000;
+
+}
+
+/*
+ * Appelé quand une entrée de GPIO reçois une IT
+ * called when an input GPIO receive an IT
+ */
+static void stm32_gpio_in_recv(void * opaque, int numPin, int level)
+{
+    assert(numPin>=0 && numPin<NB_PIN);
+    stm32_gpio_state *s = (stm32_gpio_state *) opaque;
+    
+    
+    uint32_t mask = (1 << (numPin*2)) | (1 << ((numPin*2) + 1));
+    //check if the pin is confihured as input
+    //--Pull-up Pull-Down -> must be different trom 00
+    if((s->pupd & mask) == 0) {return;} 
+    //--Mode register -> 00
+    if((s->mode & mask) != 0) {return;} 
+    
+    //Writing to the register input data register
+    printf("GPIO_in[%d]->%d\n", numPin, level);
+    if(level) {
+        s->ind |= (1 << numPin); // last => 1
+    } else {
+        s->ind &= ~(1 << numPin); //Mise à 0
+    }
+}
+
+
+
+static const MemoryRegionOps gpio_ops = {
+    .read  = stm32_gpio_read,
+    .write = stm32_gpio_write,
+    .endianness = DEVICE_NATIVE_ENDIAN,
+};
+
+
+
+static int stm32_gpio_init(SysBusDevice *dev, const unsigned char id)
+{
+    stm32_gpio_state *s = FROM_SYSBUS(stm32_gpio_state, dev);
+    s->id = id;
+    
+    // initialisation of the memory range
+    memory_region_init_io(&s->iomem, &gpio_ops, s, "gpio", 0x17FF);
+    sysbus_init_mmio(dev, &s->iomem);
+    
+    // initial the pins
+    qdev_init_gpio_in(&dev->qdev, stm32_gpio_in_recv, NB_PIN);
+    qdev_init_gpio_out(&dev->qdev, s->irq_out, NB_PIN);
+
+    stm32_gpio_reset(s);
+    vmstate_register(&dev->qdev, -1, &vmstate_stm32_gpio, s);
+
+    return 0;
+}
+
+
+
+static int stm32_gpio_init_A(SysBusDevice *dev)
+{
+    return stm32_gpio_init(dev, 'A');
+}
+
+
+
+static int stm32_gpio_init_B(SysBusDevice *dev)
+{
+    return stm32_gpio_init(dev, 'B');
+}
+
+
+
+static Property stm32_gpio_sysbus_properties[] = {
+    //XXX: DEFINE_PROP_CHR("chardev", NULL, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+
+
+static void stm32_gpio_class_init_A (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init   = stm32_gpio_init_A;
+    dc->desc  = "STM32 GPIO A";
+    dc->vmsd  = &vmstate_stm32_gpio;
+    dc->props = stm32_gpio_sysbus_properties;
+
+}
+
+
+
+static void stm32_gpio_class_init_B (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init   = stm32_gpio_init_B;
+    dc->desc  = "STM32 GPIO B";
+    dc->vmsd  = &vmstate_stm32_gpio;
+    dc->props = stm32_gpio_sysbus_properties;
+
+}
+
+
+
+static TypeInfo stm32_gpioA_info = {
+    .name       = "stm32_gpio_A",
+    .parent     = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof (stm32_gpio_state),
+    .class_init = stm32_gpio_class_init_A,
+};
+
+
+
+static TypeInfo stm32_gpioB_info = {
+    .name       = "stm32_gpio_B",
+    .parent     = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof (stm32_gpio_state),
+    .class_init = stm32_gpio_class_init_B,
+};
+
+
+
+static void stm32_gpio_register_devices(void) {
+    type_register_static(&stm32_gpioA_info);
+    type_register_static(&stm32_gpioB_info);
+}
+
+
+
+type_init(stm32_gpio_register_devices)
-- 
1.7.9.5

From 59ec269e532d85be4af08ab993ecf748513f4311 Mon Sep 17 00:00:00 2001
From: xatier <xatierlike@gmail.com>
Date: Tue, 14 Aug 2012 14:53:20 +0800
Subject: [PATCH 2/2]     add button and leds for stm32

---
 hw/arm/Makefile.objs |    2 +-
 hw/stm32_button.c    |  125 ++++++++++++++++++++++++++++++++++++++++++
 hw/stm32_led.c       |  148 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 274 insertions(+), 1 deletion(-)
 create mode 100644 hw/stm32_button.c
 create mode 100644 hw/stm32_led.c

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 15aff03..58f2894 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -9,7 +9,7 @@ obj-y += exynos4210_pmu.o exynos4210_mct.o exynos4210_fimd.o
 obj-y += exynos4210_rtc.o exynos4210_i2c.o
 obj-y += arm_mptimer.o a15mpcore.o
 obj-y += armv7m.o armv7m_nvic.o stellaris.o stellaris_enet.o
-obj-y += stm32.o stm32_gpio.o 
+obj-y += stm32.o stm32_gpio.o stm32_button.o stm32_led.o
 obj-y += highbank.o
 obj-y += pxa2xx.o pxa2xx_pic.o pxa2xx_gpio.o pxa2xx_timer.o pxa2xx_dma.o
 obj-y += pxa2xx_lcd.o pxa2xx_mmci.o pxa2xx_pcmcia.o pxa2xx_keypad.o
diff --git a/hw/stm32_button.c b/hw/stm32_button.c
new file mode 100644
index 0000000..33df31b
--- /dev/null
+++ b/hw/stm32_button.c
@@ -0,0 +1,125 @@
+
+#include "sysbus.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    uint8_t buttonState;
+    qemu_irq gpio_out;
+    CharDriverState* chr;
+    const char *id;
+} stm32_button_state;
+
+
+
+static const VMStateDescription vmstate_stm32_button = {
+    .name       = "stm32_button",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[])
+    {
+        VMSTATE_UINT8(buttonState, stm32_button_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+
+static void stm32_button_reset(stm32_button_state *s)
+{
+    s->buttonState = 0;
+}
+
+
+
+static int stm32_can_receive(void *opaque) { return 1; } // always return 1
+
+
+
+static void stm32_receive(void *opaque, const uint8_t* buf, int size)
+{
+
+    stm32_button_state *s = (stm32_button_state *) opaque;
+    
+    int i = 0;
+    for(i = 0; i < size; i++) {
+        //Leve une IRQ
+        uint8_t etat   = buf[i];
+        s->buttonState = etat;
+        printf("the BUTTON change state->%d\n", (int)etat);
+        qemu_set_irq(s->gpio_out, (int)etat);
+    }
+}
+
+
+
+static void stm32_event(void *opaque, int event) { } // NOT USED
+
+
+
+static int stm32_button_init(SysBusDevice *dev, const char* id)
+{
+    stm32_button_state *s = FROM_SYSBUS(stm32_button_state, dev);
+    
+    // initial the output pins
+    qdev_init_gpio_out(&dev->qdev, &s->gpio_out, 1);
+    
+    // inital the char dev
+    s->chr = qemu_char_get_next_serial();
+    s->id  = id;
+
+    if (s->chr) {
+        qemu_chr_add_handlers(s->chr, stm32_can_receive, stm32_receive, stm32_event, s);
+    }
+    
+    stm32_button_reset(s);
+    vmstate_register(&dev->qdev, -1, &vmstate_stm32_button, s);
+ 
+    return 0;
+}
+
+
+
+static int stm32_button_init_(SysBusDevice *dev)
+{
+    return stm32_button_init(dev, "user0");
+}
+
+
+
+static Property stm32_button_sysbus_properties[] = {
+    //XXX: DEFINE_PROP_CHR("chardev", NULL, chr),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+
+
+static void stm32_button_class_init (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init   = stm32_button_init_;
+    dc->desc  = "STM32 Button";
+    dc->vmsd  = &vmstate_stm32_button;
+    dc->props = stm32_button_sysbus_properties;
+
+}
+
+
+
+static TypeInfo stm32_button_info = {
+    .name       = "stm32_button",
+    .parent     = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof (stm32_button_state),
+    .class_init = stm32_button_class_init,
+};
+
+
+
+static void stm32_button_register_devices(void) {
+    type_register_static(&stm32_button_info);
+}
+
+
+
+type_init(stm32_button_register_devices)
diff --git a/hw/stm32_led.c b/hw/stm32_led.c
new file mode 100644
index 0000000..51f87e7
--- /dev/null
+++ b/hw/stm32_led.c
@@ -0,0 +1,148 @@
+
+#include "sysbus.h"
+
+typedef struct {
+    SysBusDevice busdev;
+    uint8_t ledState;
+    CharDriverState *chr;
+    const char *id;
+} stm32_led_state;
+
+
+
+static const VMStateDescription vmstate_stm32_led = {
+    .name       = "stm32_led",
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .fields = (VMStateField[])
+    {
+        VMSTATE_UINT8(ledState, stm32_led_state),
+        VMSTATE_END_OF_LIST()
+    }
+};
+
+
+
+static void stm32_led_reset(stm32_led_state *s)
+{
+    s->ledState = 0;
+}
+
+/*
+ * Appelé quand une entrée de led reçoit une IT
+ */
+static void stm32_led_recvirq(void * opaque, int numPin, int level)
+{
+    // the LED change state
+    printf("The LED change state->%d\n", level);
+    stm32_led_state *s = (stm32_led_state *) opaque;
+    
+    if(s->chr) {
+        uint8_t buffer = (uint8_t)level;
+        qemu_chr_fe_write(s->chr, &buffer, 1);
+    }
+}
+
+
+
+static int stm32_led_init(SysBusDevice *dev, const char* id)
+{
+    stm32_led_state *s = FROM_SYSBUS(stm32_led_state, dev);
+    
+    
+    // debug message
+    // printf("%p, %d\n", &dev->qdev, dev->qdev.num_gpio_out);
+
+    // initial the output pins
+    qdev_init_gpio_in(&dev->qdev, stm32_led_recvirq, 1);
+
+    // initial the char device
+    s->chr = qemu_chr_find(id);
+    s->id  = id;
+    
+    stm32_led_reset(s);
+    vmstate_register(&dev->qdev, -1, &vmstate_stm32_led, s);
+
+    return 0;
+}
+
+ 
+
+static int stm32_led_init_Blue(SysBusDevice *dev)
+{
+    return stm32_led_init(dev, "led_blue");
+}
+
+
+
+static int stm32_led_init_Green(SysBusDevice *dev)
+{
+    return stm32_led_init(dev, "led_green");
+}
+
+
+
+static Property stm32_led_blue_sysbus_properties[] = {
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+
+
+static Property stm32_led_green_sysbus_properties[] = {
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+
+
+static void stm32_led_blue_class_init (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init   = stm32_led_init_Blue;
+    dc->desc  = "STM32 LED Blue";
+    dc->vmsd  = &vmstate_stm32_led,
+    dc->props = stm32_led_blue_sysbus_properties;
+}
+
+
+
+static void stm32_led_green_class_init (ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+    k->init   = stm32_led_init_Green;
+    dc->desc  = "STM32 LED Green";
+    dc->vmsd  = &vmstate_stm32_led,
+    dc->props = stm32_led_green_sysbus_properties;
+}
+
+
+
+static TypeInfo stm32_led_info_blue = {
+    .name       = "stm32_led_blue",
+    .parent     = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof (stm32_led_state),
+    .class_init = stm32_led_blue_class_init,
+};
+
+
+
+static TypeInfo stm32_led_info_green = {
+    .name       = "stm32_led_green",
+    .parent     = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof (stm32_led_state),
+    .class_init = stm32_led_green_class_init,
+};
+
+
+
+static void stm32_led_register_devices(void) {
+    type_register_static(&stm32_led_info_blue);
+    type_register_static(&stm32_led_info_green);
+}
+
+
+
+type_init(stm32_led_register_devices)
-- 
1.7.9.5


很開心的把生出來的 patch 寄給 jserv review,馬上被抓出以下有問題的地方QAQ

看來開發功力還要再練練!!慢慢養成經驗值



注意以下:
(1) 使用 C-style comment 風格
(2) 錯別字,如 "unimplemtmented"
(3) 文法,如 "initial the processor" 應該是 "initialize the processor"
(4) "create the button" 與 "create the LEDs" 應該獨立為其他 patch
(5) ssys_{write,read} 是否需要實做?


=======================================
8/20 update

把我的 code 放上 github:  https://github.com/xatier/qemu

以後改完 code 要先XD

git push qemu-github summer-work

另外 rebase origin 要記得先 pull 下來... merge,不然會失敗XD

筆記一下流程


git checkout master                                                             
git fetch origin                                                                
git pull                                                                        
git checkout summer-work                                                        
git rebase origin/master                                                        
git pull qemu-github summer-work                                                
git merge                                                                       
git push qemu-github summer-work 

=======================================

目前的問題是卡在實作 RCC 的部份,參考了 pl031 / stellaris 等機器正在想辦法實作


想辦法加上 RCC tick 的行為,透過 qemu_new_timer_ns 去初始化 tick

接上 qemu-timer.h 上面的 API




另外有這台歪果人做的 STM32 模擬,好像跟我們的很像...


https://github.com/beckus/qemu_stm32/commits/stm32_v1.1.0/
https://github.com/beckus/stm32_p103_demos


不過 beckus 的 code 最近在大改就是 (應該是要 fit 新的 qemu 版本)



=======================================
9/23 update

後來進開學比較忙,一方面  qemu 的進度比較少,令一方面也沒時間再把這些瑣碎的小進度放上來

接下來的工作是幫忙 jserv 測試 beckus 的作品,我這邊目前應該暫時停止開發了

前端的部份 WeiYao 會幫忙做網頁式的模擬器外觀,任務是要把原本的 Python-TKinter 的部份重新做過,用 HTML5


jserv 在成大的課程也陸續開始了,課程網頁:


作業(使用 gitcafe!):


雖然很可惜最後沒能在暑假前幫 jserv 完成完整的 qemu-STM32 的 modeling ,但是還是很開心,在這期間學到了非常非常多的東西,也感謝 jserv  能給我一個這麼棒的打工經驗!

希望接下來還有機會跟 jserv 一起合作其他相關有趣的專案 :)


開發暫且告一段落囉~




No comments:

Post a Comment