2015年10月10日 星期六

Rust 的 C 語言界面:struct 和 enum

對於打算使用新語言的人來說,能否容易地使用 C 語言大量的函式庫是選擇語言的重要指標。

使用 C 的函式庫,簡單分兩個層次:
  1. 呼叫 C 的函式,以及提供 callback
  2. 使用 C 的資料結構。
過往的經驗告訴我,麻煩在第二點。不同語言的資料結構採用不同的記憶體 layout,因此很難直接使用他種語言的資料結構。於是常要先撰寫一堆存取資料結構的小函式,再透過這些函式來使用資料結構。

這假日我好好研究了這個問題,驚奇發現 Rust 是僅次於 C++,我用過最容易和 C 界接的語言。以下以我最近處理的一個 struct 裡面有 struct,還有 enum,更有陣列的資料結構的簡化版,說明如何做出有同樣 layout 的 Rust 資料結構。

#define EC_MAX_STRING_LENGTH 64
#define EC_MAX_PORTS 4

typedef enum {
    EC_PORT_NOT_IMPLEMENTED, 
    EC_PORT_NOT_CONFIGURED, 
    EC_PORT_EBUS, 
    EC_PORT_MII 
} ec_slave_port_desc_t;

typedef struct {
    uint8_t link_up; 
    uint8_t loop_closed; 
    uint8_t signal_detected; 
} ec_slave_port_link_t;

typedef struct {
    uint16_t position; 
    uint32_t vendor_id; 
    uint32_t product_code; 
    struct {
        ec_slave_port_desc_t desc; 
        ec_slave_port_link_t link; 
        uint32_t receive_time; 
        uint16_t next_slave; 
    } ports[EC_MAX_PORTS]; 
    uint8_t error_flag; 
    char name[EC_MAX_STRING_LENGTH]; 
} ec_slave_info_t;

以下使用 Rust 做出在記憶體 layout 一模一樣的資料結構:

// 首先要使用 libc 中提供的 C 的型別。
// int  對應 c_int
// uint 對應 c_uint
// char 對應 u8
// int16_t, uint16_t 等直接對應 int16_t 及 uint16_t。

extern crate libc;
use libc::{c_uint, c_int, int16_t, uint8_t, uint16_t, uint32_t};

const EC_MAX_PORTS: usize = 4;
const EC_MAX_STRING_LENGTH: usize = 64;

// 在 enum 前加上 #[repr(C)],告知 Rust 採用 C 的 layout。

#[repr(C)]
pub enum ec_slave_port_desc_t {
    EC_PORT_NOT_IMPLEMENTED = 0,
    EC_PORT_NOT_CONFIGURED,
    EC_PORT_EBUS,
    EC_PORT_MII,
}

#[repr(C)]
pub struct ec_slave_port_link_t {
    link_up: uint8_t,
    loop_closed: uint8_t,
    signal_detected: uint8_t,
}

// 對於 C 結構中的陣列,就使用 Rust 的陣列取代。
#[repr(C)]
pub struct ec_slave_info_t {
    pub position: uint16_t,
    pub vendor_id: uint32_t,
    pub product_code: uint32_t,
    ports: [ec_slave_port_t; EC_MAX_PORTS],
    al_state: uint8_t,
    error_flag: uint8_t,
    name: [u8;EC_MAX_STRING_LENGTH],
}

我產生上列程式的組合語言列表,確定它和 C 語言的記憶體配置一模一樣。 比較麻煩的是給預設值初始化時,這部份以後有機會再說明。

2015年1月31日 星期六

BotBone: Reactive image with D3.js

假日中為我們的 BotBone 計畫設計一款互動式界面。我先以手機拍照,以 Inkscape 將照片內嵌到 SVG 檔中。再以 D3.js 加上互動元件。

初步成果如下圖:
這工作耗費我兩天的時間,接下來就要讀取 BotBone 的 I/O,並依據實體 I/O 的狀態,動態地改變圖片上的互動元件。

未來希望能將這類工作簡化。最好一小時就能完成。

2015年1月5日 星期一

Debian 下的 PostgreSQL

開始建公司網頁。決定採用 Yahoo 的 Isomorphic React 作為前後端的架構。資料庫則使用剛出來的 PostgreSQL 9.4。

我使用 Debian 7,閱讀 /usr/share/doc/postgresql-9.4/README.Debian.gz 讓我很快能建立一個 role 及一個 database。但是建立出來的 role 沒有自己的 password。因此還是乖乖地閱讀 PostgreSQL 文件,並上網查資料。以下是簡單的報告。

首先先依 README.Debian 建立 role 和 database:
$sudo -u postgres bash
$createuser -DRS joe
$createdb -O joe joework
當執行 psql 時,
$psql -U joe -W joework
因為沒有 joe 的 password,所以無法進入。 因為 createuser 時未給 password。
重新來過:
$dropdb joework
$dropuser joe
$createuser -DPRS joe
$createdb -O joe joework
當執行 psql 時,
$psql -U joe -W joework
psql: FATAL:  Peer authentication failed for user "joe"
修改 /etc/postgresql/9.4/main/pg_hba.conf 中的 Authentication method:
# "local" is for Unix domain socket connections only
#local   all             all                                     peer
local   all             all                                     md5
重新啟動 postgresql,因為我未使用 systemd,因此執行:
$sudo service postgresql restart
再次執行 psql,
$psql -U joe -W joework
成功。

2014年12月29日 星期一

J1 Forth CPU 研究之五:算術邏輯單元(ALU)

這是一系列文章中的第五篇,這系列的文章是要解讀 J1 CPU 的設計以及在其上的 Forth 語言的使用法。

這篇談的是 j1 CPU 的 Verilog 程式中的算術邏輯單元(ALU),請見以下檔案:

src/hardware/verilog/j1.v


為了方便說明,以下程式的次序不一定和 j1.v 中一致。

  wire [15:0] insn;
  wire [15:0] immediate = { 1'b0, insn[14:0] };
  wire is_alu = (insn[15:13] == 3'b011);
  wire is_lit = (insn[15]);


  reg [3:0] st0sel;
  always @*
  begin
    case (insn[14:13])
      2'b00: st0sel = 0;          // ubranch
      2'b01: st0sel = 1;          // 0branch
      2'b10: st0sel = 0;          // call
      2'b11: st0sel = insn[11:8]; // ALU
      default: st0sel = 4'bxxxx;
    endcase
  end



  always @*
  begin
    if (insn[15])
      _st0 = immediate;
    else
      case (st0sel)
        4'b0000: _st0 = st0;
        4'b0001: _st0 = st1;
        4'b0010: _st0 = st0 + st1;
        4'b0011: _st0 = st0 & st1;
        4'b0100: _st0 = st0 | st1;
        4'b0101: _st0 = st0 ^ st1;
        4'b0110: _st0 = ~st0;
        4'b0111: _st0 = {16{(st1 == st0)}};
        4'b1000: _st0 = {16{($signed(st1) < $signed(st0))}};
        4'b1001: _st0 = st1 >> st0[3:0];
        4'b1010: _st0 = st0 - 1;
        4'b1011: _st0 = rst0;
        4'b1100: _st0 = |st0[15:14] ? io_din : ramrd;
        4'b1101: _st0 = st1 << st0[3:0];
        4'b1110: _st0 = {rsp, 3'b000, dsp};
        4'b1111: _st0 = {16{(st1 < st0)}};
        default: _st0 = 16'hxxxx;
      endcase
  end

以上總共 40 行,加上之前的 90 行,我們已經看懂了 j1.v 的 130/200。接下來的兩篇處理RAM 及 memory mapped I/O,我們對 j1.v 的探索就算完成。

2014年4月1日 星期二

Mapabone 硬體設計之一

目前正在設計一款針對創客社群,類似 Raspberry Pi 和 Beaglebone,但是更強調 realtime 性能以及運動控制的軟硬體平台。這個軟硬體平台暫時命名為 Mapabone。目前的硬體設計原則如下:

  1. 參考 Beaglebone 的設計,但針對其複雜不便之處加以簡化。
  2. 擁抱 Raspberry Pi 的 pin definition 以擁抱 Raspberry Pi 社群。
  3. 擴充更多的 I/O 腳位以方便設計週邊。
  4. 內建 FPGA 以實現 MPU 無法達到的性能。
此外還進行其他的修正,以附合我心中好用的運動控制硬體平台的形象。

I'm designing for makers a software/hardware platform, which likes Raspberry Pi or Beaglebone, but emphasize realtime performance and motion control. I named this software/hardware platform Mapabone temporarily. My design guidelines are:

  1. Adapt the design of Beaglebone, but simplify its complexity.
  2. Embrace the P1 pin definition of Raspberry Pi, to embrace the Raspberry Pi community.
  3. Add more connectors to facilitate the development of peripherials.
  4. Embed an FPGA to provide performance which can not be achieved with MPU.
Many other improvements are underway in order to realize the ideal motion control platform in my mind.

進行這設計已有一段時間。已完成腳位的分析,開始進行線路的修改。但因為我從未使用過 OrCAD,在此一系列「Mapabone 硬體設計筆記」中記錄我在修改線路時的各種經驗,以及對這硬體平台的想法。

第零課:New project 和 New design

首先學習建立一個新的 Project 和 Schematics。選擇 File->New->Project...
輸入 project 名稱及位置,並選擇要進行的設計是 Schematic。
這樣就可以開始了!

第一課:Place Part

就是把零件放在設計圖面上,再用線連起來。我學會了置放以下的零件:電阻(R)、電容(C)、電感(L)、電源(V)。作法很簡單,選擇 Place->Part,再選擇零件。比較難的是要知道如何找到這些零件。最快的方法是輸入零件的第一個字母再在清單中尋找。以下是我們用的元件。

  • R/ANALOG
  • C/ANALOG
  • L/ANALOG
  • VPULSE/SOURCE

再來是接線和地線,分別選擇 Place->Wire 和 Place->Ground。

以下是我的完成圖:



2013年10月12日 星期六

學習筆記:Debian on Beaglebone

首先參考 Compile Linux Kernel 3.2 for Arm and Emulate with QEMU
這篇文章中不討論 u-boot, 因此編譯的核心是 zImage 而非 uImage。只利用 initramfs 執行一個很小的 init 程式。

安裝 Emdebian Toolchain

再來參考 How to Cross Compile Arm Kernel under Ubuntu-10-10
這篇講如何編譯 Ubuntu,但是同樣的作法用在 Debian 不成功。末尾說明了如何編從 kernel.org 拿到的核心,到是可以用的。只是沒說明編了 uImage 後要如何和 u-boot 結合。

之後參考 Installing Debian On TI BeagleBone
這篇使用 linaro-image-tools 和 live-build,以及 qemubuilder 。
依照文中的方法建造 Debian binary rootfs 不成功。
之後再試建造 u-boot,但之前使用 qemubuilder 失敗過,得到以下訊息,
   FATAL: kernel too old
據說是因 c 函式庫的新舊問題造成,我沒時間思考如何解決,因此先放棄。
建造 kernel 也使用 qemubuilder,因此放棄。
再來是 hwpack。這部份不熟,先擱置。

再參考 BeagleBone Debian Wheezy snapshot armhf based RootFileSystem
先取得 cross-compilers:
   $ wget -c https://launchpad.net/linaro-toolchain-binaries/trunk/2013.07/+download/gcc-linaro-arm-linux-gnueabihf-4.8-2013.07-1_linux.tar.xz
   $ tar xJf gcc-linaro-arm-linux-gnueabihf-4.8-2013.07-1_linux.tar.xz
   $ export CC=`pwd`/gcc-linaro-arm-linux-gnueabihf-4.8-2013.07-1_linux/bin/arm-linux-gnueabihf-
確定是 32bit 版本:
   $ {CC}gcc --version
取得最新版的 u-boot:
   $ git clone git://git.denx.de/u-boot.git
   $ cd u-boot/
   $ git checkout v2013.07 -b tmp
patch 後進行編譯:
   $ wget -c https://raw.github.com/eewiki/u-boot-patches/master/v2013.07/0001-am335x_evm-uEnv.txt-bootz-n-fixes.patch
   $ patch -p1 < 0001-am335x_evm-uEnv.txt-bootz-n-fixes.patch
   $ make ARCH=arm CROSS_COMPILE=${CC} distclean
   $ make ARCH=arm CROSS_COMPILE=${CC} am335x_evm_config
   $ make ARCH=arm CROSS_COMPILE=${CC}
接下來編譯 kernel,在此不使用作者提供的 git.sh, 因曾無法 git clone 或 fetch from kernel.org 而出問題。而且未來將針對特定的 branch 進行 patch。作法如下:

。不使用文中所用的 git.sh,因此先 clone 之後,修改 system.sh 中的 LINUX_GIT,並且將 scripts/git.sh 中所有從 kernel.org 取得 repository 的程式片段都放在註解中。之後就可以成功編譯。
可惜的是我在成功產生一個  3.2 的 MicroSD 後,以此 MicroSD boot linux,在 boot kernel  階段失敗。當掉的原因是因為我實際上產生的是 3.12 的 kernel 而非 3.2 的 kernel。
在 linux-dev 中執行
   git checkout origin/am33x-v3.2 -b tmp
並在 linux-src 中執行
    git checkout v3.2
後,成功。

再參考 Flashing Ubuntu 13-04 or debian wheezy to the beaglebone black emmc

2013年9月4日 星期三

YUI 的 Promise

YUI 的 Promise 用法如下:

var promise = new Y.Promise(function (resolve, reject) {
   // 非同步的呼叫, 當非同步的呼叫完成後,
   // 依結果執行 resolve 或是 reject。
}

在內部的實作中,我們可見 Y.Promise 定義了一個 Y.Promise.Resolver,再使用這個 resolver
來執行以上傳給 Y.Promise 的 function。

fn.call (this, function(value) {
      resolver.fulfill(value);
   }, function (value) {
      resolver.reject(value);
   }
}):

因此可知 resolve(value) 就是執行 resolver.fulfill(value), reject(value) 就是執行 resolver.reject(value)。

所以可把對 Y.Promise 的說明改成如下:

var promise = new Y.Promise(function (resolve, reject) {
   // 非同步的呼叫, 當非同步的呼叫完成後,
   // 依結果執行 resolver.fulfill 或是 resolver.reject。
}

由於看 YUI promise 以上程式時有點吃力,因此將理解記錄於此。