ParallellaのFPGAに独自の回路を追加して、その回路をCPUから制御できるようにします。
FPGA初心者向けにFPGA関連情報へのリンクをまとめたページを公開しました。「ZEROからのFPGA : FPGAの基礎知識」こちらも参考にしてください。
基本設計
Parallellaに搭載されているXilinx社のZynqチップは、デュアルコア ARM Cortex A9 CPUとFPGAを統合したSoC(System-on-a-chip)です。
チップ内の各デバイスの接続にはARM社の提唱するAMBA(Advanced Microcontroller Bus Architecture) AXI(Advanced eXtensible Interface)バス・アーキテクチャが採用されており、基本的にCPUとFPGAに実装する回路はこのインターフェースを介して通信を行うことになります。
Zynqブロック図
通信の起点となる側にはAXI Masterインターフェースを、リクエストを受ける側にはAXI Slaveインターフェースを実装します。
今回はFPGAの回路側にAXI4-Lite Slaveインターフェースを実装し、CPU側からはメモリー・マップト・I/Oでアクセスして制御します。AXI4-LiteはAXI4のサブセット版で、実装を容易にするために機能が簡略化されています。
SlaveはMasterからのリクエストに応じてWrite、Readの処理を行います。(図では省略していますが、実際はWrite時もSlave側から「データ受け取り完了」を示す信号が返ります。)Slaveは常に受身の状態で動作し、自分の方からデータを読み書きすることはできません。
ユーザー回路に割り当てる物理アドレスは自由に決められますが、Parallella標準の設計では0x60000000から広い空きエリアがあるので、そこを使うことにします。
プロジェクト作成
「ParallellaのFPGAビットストリームを生成する」で作成したプロジェクトをさらにテンプレートにして、メニュー→「File」→「Save Project As...」で「parallella_hw_projects」ディレクトリ以下に新しいプロジェクトを作成してください。
再度複製したプロジェクトでは、「version.v」と「system.xmp」はすでにImportされた状態になっているはずです。
「getbits」、「bit2bin.bif」、「dummy.elf」ファイルと「bitbin」ディレクトリは「Save Project As...」ではコピーされないため、手動で新しいプロジェクトのディレクトリにコピーし、修正します。詳しくは「ParallellaのFPGAビットストリームを生成する:bit.binファイルの生成」を参照してください。
サンプルIPコアのダウンロードと展開
AXI4-Lite Slaveインターフェースを使った簡単なIPコア(Intellectual Property Core:再利用可能な機能ブロック)を作りました。二つのパラメーターを読み込んで、その和を返すだけのHelloWorld的なサンプルです。これをParallellaのビットストリームに組み込みます。
端末で、
cd ~/parallella_hw_projects
wget https://cellspe.matrix.jp/files/hello_axi.tar.gz
tar xzf hello_axi.tar.gz
cd hello_axi
mkdir -p ../user_ip/edk/pcores
mv hello_axi_v1_00_a ../user_ip/edk/pcores/
IPリポジトリを追加
Design Sources→parallella_z7_top→system_stub→system.xmpをダブルクリックするか、右クリックして「Open File」を選択します。
XPSのライセンス・エラーは「OK」、「Close」で閉じます。
XPSが起動したら「Add/Remove IP Repositories」ボタンをクリックします。
IPのファイルが格納されているディレクトリ(IPリポジトリ)を設定します。
「/mnt/windowsC/Adapteva...」はゴミデータなので選択して「×」ボタンをクリックして削除します。
次に、「+」ボタンをクリックします。
ディレクトリ選択画面が開くので「~/parallella_hw_projects/user_ip」ディレクトリを選択して「Choose」をクリックします。元の画面に戻るので「OK」で閉じます。
IPリポジトリに変更を加えた時には「Rescan User IP Repositories」ボタンをクリックします。
IPの追加と接続
「IP Catalog」を下の方にスクロールさせると「Project Peripheral Repository1」が出来ているのでその中の「User」→「Hello AXI」を右クリックして「Add IP」を選択します。確認ダイアログが出るので「Yes」で進めます。
これはIPのコンフィギュレーション画面ですが今回は特に設定する必要がないのでそのまま「OK」をクリックします。
IPの接続を自動で行うか手動で行うかの選択画面です。自動で行うと意図しない設定が行われてしまう場合があるので手動で設定します。「User will make necessary connections and settings」を選択して「OK」をクリックします。
「System Assembly View」の「Bus Interfaces」タブをクリックするとこの画面が出ます。ここでバスの接続を設定します。Processing_system7_0→M_AXI_GP0(AXI Master General Purpose ポート)がaxi_interconnect_1に接続されていることが分かります。これに「Hello AXI」を接続する設定をします。
「hello_axi_0」の「+」をクリックし、S_AXI(AXI Slave)の「No connection」と書かれている部分をクリックします。
「axi_interconnect_1」を選択して「OK」をクリックします。
hello_axi_0がaxi_interconnect_1を経由してM_AXI_GP0に接続されました。
「System Assembly View」の「Ports」タブをクリックしてポート接続設定を開きます。「axi_interconnect_1」を展開するとクロックのポートである「INTERCONNECT_ACLK」に「processing_system7_0::FCLK_CLK0」が接続されていることが分かります。hello_axi_0にも同じクロックを供給します。
「hello_axi_0」を展開し、「S_AXI_ACLK」の「Connected Port」欄のアイコンをクリックします。
「processing_system7_0」の「FCLK_CLK0」を選択してチェックマークをクリックします。
「System Assembly View」の「Address」タブをクリックしてメモリマップの設定を開きます。
「Unmapped Addresses」を展開して「hello_axi_0」の「Base Address」の欄をクリックして「0x60000000」と入力します。
「hello_axi_0」の行の右端の「Lock」にチェックを入れます。
「Perform Project Design Rule Checks」ボタンをクリックしてIPの接続にエラーがないか確認します。下のメッセージ欄にいくつかWARNINGが出ますがErrorが出なければOKです。
メニュー→「File」→「Exit」でXPSを終了し、PlanAheadに戻ります。
IPのソースコードをプロジェクトに追加
PlanAheadでソースコードを編集できるように、IPのソースコードをプロジェクトに追加します。
「Add Sources」ボタンをクリックします。
「Add or Create Design Sources」を選択して「Next」をクリックします。
「Add Files」ボタンをクリックします。
IP本体のソースコードを追加します。
~/parallella_hw_projects/user_ip/edk/pcores/hello_axi_v1_00_a/hdl/verilog/hello_axi.v を選択して「OK」をクリックします。
「Copy sources into project」はチェックを付けないで「Finish」をクリックします。
もう一度「Add Sources」ボタンをクリックし、今度は「Add or Create Simulation Sources」を選択して「Next」をクリックします。
「Add Files」ボタンをクリックします。
シミュレーション用のソースコードを追加します。
~/parallella_hw_projects/hello_axi/hello_axi_testbench/hello_axi_testbench.v を選択して「OK」をクリックします。
「Copy sources into project」はチェックを付けないで「Finish」をクリックします。
ソースコードの閲覧、編集
プロジェクトに追加したソースコードは「Sources」ウィンドウでファイル名をダブルクリックすると内蔵エディタで開くことができます。ソースコードはVerilog HDLで書いています。Parallellaの公式ビットストリームのロジックも基本的にVerilog HDLで書かれています。
このIPのソースコードはBSD 2-Clauseライセンスで公開します。
IP本体:hello_axi.v
/*
Copyright (c) 2014, miya
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
module hello_axi
#(
// user parameters
parameter C_STATE_S_ADDR = 3'd0,
parameter C_STATE_S_W_DATA = 3'd1,
parameter C_STATE_S_W_RESPONSE = 3'd2,
parameter C_STATE_S_R_DATA = 3'd3,
parameter C_STATE_S_R_READY = 3'd4,
parameter C_MAP_LEN_IN_BITS = 32'd2,
parameter C_MAP_ADDR_MASK = (32'd1 << (C_MAP_LEN_IN_BITS + 32'd2)) - 32'd1,
parameter C_MAP_ADDR_MASK_N = ~C_MAP_ADDR_MASK,
// system parameters (will be overwritten by XPS)
parameter C_S_AXI_DATA_WIDTH = 32,
parameter C_BASEADDR = 32'hFFFFFFFF,
parameter C_HIGHADDR = 32'h00000000
)
(
input S_AXI_ACLK,
input S_AXI_ARESETN,
input [31:0] S_AXI_AWADDR,
input [2:0] S_AXI_AWPROT,
input S_AXI_AWVALID,
output S_AXI_AWREADY,
input [31:0] S_AXI_WDATA,
input [3:0] S_AXI_WSTRB,
input S_AXI_WVALID,
output S_AXI_WREADY,
output [1:0] S_AXI_BRESP,
output S_AXI_BVALID,
input S_AXI_BREADY,
input [31:0] S_AXI_ARADDR,
input [2:0] S_AXI_ARPROT,
input S_AXI_ARVALID,
output S_AXI_ARREADY,
output [31:0] S_AXI_RDATA,
output [1:0] S_AXI_RRESP,
output S_AXI_RVALID,
input S_AXI_RREADY
);
// output port registers
reg s_axi_awready;
reg s_axi_wready;
reg s_axi_bvalid;
reg s_axi_arready;
reg [31:0] s_axi_rdata;
reg s_axi_rvalid;
// user registers
reg [2:0] state_s;
reg [31:0] s_address;
reg [31:0] reg_a;
reg [31:0] reg_b;
// output port assignment
assign S_AXI_AWREADY = s_axi_awready;
assign S_AXI_WREADY = s_axi_wready;
assign S_AXI_BRESP = 2'b00;
assign S_AXI_BVALID = s_axi_bvalid;
assign S_AXI_ARREADY = s_axi_arready;
assign S_AXI_RDATA = s_axi_rdata;
assign S_AXI_RRESP = 2'b00;
assign S_AXI_RVALID = s_axi_rvalid;
// AXI4-Lite slave
always @(posedge S_AXI_ACLK)
begin
if (S_AXI_ARESETN == 1'b0)
begin
s_axi_awready <= 1'b0;
s_axi_wready <= 1'b0;
s_axi_bvalid <= 1'b0;
s_axi_arready <= 1'b0;
s_axi_rvalid <= 1'b0;
state_s <= C_STATE_S_ADDR;
end
else
begin
case (state_s)
C_STATE_S_ADDR:
begin
// address decode
if ((S_AXI_AWVALID == 1'b1) && ((S_AXI_AWADDR & C_MAP_ADDR_MASK_N) == C_BASEADDR))
// write: get the address
begin
s_address <= S_AXI_AWADDR;
s_axi_awready <= 1'b1;
state_s <= C_STATE_S_W_DATA;
end
else if ((S_AXI_ARVALID == 1'b1) && ((S_AXI_ARADDR & C_MAP_ADDR_MASK_N) == C_BASEADDR))
// read: get the address
begin
s_address <= S_AXI_ARADDR;
s_axi_arready <= 1'b1;
state_s <= C_STATE_S_R_DATA;
end
end
// write: get the data
C_STATE_S_W_DATA:
if (S_AXI_WVALID == 1'b1)
begin
case (s_address[3:2])
2'd0:
// addr: 0xXXXXXXX0
begin
// a = input data
reg_a <= S_AXI_WDATA;
end
2'd1:
// addr: 0xXXXXXXX4
begin
// b = input data
reg_b <= S_AXI_WDATA;
end
default: ;
endcase
s_axi_awready <= 1'b0;
s_axi_wready <= 1'b1;
s_axi_bvalid <= 1'b1;
state_s <= C_STATE_S_W_RESPONSE;
end
// write: wait for bready
C_STATE_S_W_RESPONSE:
if (S_AXI_BREADY == 1'b1)
begin
s_axi_wready <= 1'b0;
s_axi_bvalid <= 1'b0;
state_s <= C_STATE_S_ADDR;
end
// read: pass the data
C_STATE_S_R_DATA:
begin
case (s_address[3:2])
2'd2:
// addr: 0xXXXXXXX8
begin
// output data = c
s_axi_rdata <= reg_a + reg_b;
end
default: ;
endcase
s_axi_arready <= 1'b0;
s_axi_rvalid <= 1'b1;
state_s <= C_STATE_S_R_READY;
end
// read: wait for rready
C_STATE_S_R_READY:
if (S_AXI_RREADY == 1'b1)
begin
s_axi_rvalid <= 1'b0;
state_s <= C_STATE_S_ADDR;
end
default: ;
endcase
end
end
endmodule
テストベンチ:hello_axi_testbench.v
/*
Copyright (c) 2014, miya
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
`timescale 1ns / 1ps
module hello_axi_testbench;
parameter STEP = 10; // 100MHz
reg S_AXI_ACLK;
reg S_AXI_ARESETN;
reg [31:0] S_AXI_AWADDR;
reg [2:0] S_AXI_AWPROT;
reg S_AXI_AWVALID;
wire S_AXI_AWREADY;
reg [31:0] S_AXI_WDATA;
reg [3:0] S_AXI_WSTRB;
reg S_AXI_WVALID;
wire S_AXI_WREADY;
wire [1:0] S_AXI_BRESP;
wire S_AXI_BVALID;
reg S_AXI_BREADY;
reg [31:0] S_AXI_ARADDR;
reg [2:0] S_AXI_ARPROT;
reg S_AXI_ARVALID;
wire S_AXI_ARREADY;
wire [31:0] S_AXI_RDATA;
wire [1:0] S_AXI_RRESP;
wire S_AXI_RVALID;
reg S_AXI_RREADY;
hello_axi
#(
.C_BASEADDR ( 32'h60000000 ),
.C_HIGHADDR ( 32'h6000FFFF )
)
hello_axi_0
(
.S_AXI_ACLK ( S_AXI_ACLK ),
.S_AXI_ARESETN ( S_AXI_ARESETN ),
.S_AXI_AWADDR ( S_AXI_AWADDR ),
.S_AXI_AWPROT ( S_AXI_AWPROT ),
.S_AXI_AWVALID ( S_AXI_AWVALID ),
.S_AXI_AWREADY ( S_AXI_AWREADY ),
.S_AXI_WDATA ( S_AXI_WDATA ),
.S_AXI_WSTRB ( S_AXI_WSTRB ),
.S_AXI_WVALID ( S_AXI_WVALID ),
.S_AXI_WREADY ( S_AXI_WREADY ),
.S_AXI_BRESP ( S_AXI_BRESP ),
.S_AXI_BVALID ( S_AXI_BVALID ),
.S_AXI_BREADY ( S_AXI_BREADY ),
.S_AXI_ARADDR ( S_AXI_ARADDR ),
.S_AXI_ARPROT ( S_AXI_ARPROT ),
.S_AXI_ARVALID ( S_AXI_ARVALID ),
.S_AXI_ARREADY ( S_AXI_ARREADY ),
.S_AXI_RDATA ( S_AXI_RDATA ),
.S_AXI_RRESP ( S_AXI_RRESP ),
.S_AXI_RVALID ( S_AXI_RVALID ),
.S_AXI_RREADY ( S_AXI_RREADY )
);
// generate clock
always #(STEP / 2)
begin
S_AXI_ACLK = ~S_AXI_ACLK;
end
// fake axi master write
task fake_axi_master_write
(
input [31:0] addr,
input [31:0] data
);
begin
S_AXI_AWADDR = addr;
S_AXI_AWVALID = 1'b1;
#STEP;
S_AXI_AWVALID = 1'b0;
S_AXI_WDATA = data;
S_AXI_WVALID = 1'b1;
#STEP;
S_AXI_WVALID = 1'b0;
S_AXI_BREADY = 1'b1;
#STEP;
S_AXI_BREADY = 1'b0;
#STEP;
end
endtask
// fake axi master read
task fake_axi_master_read
(
input [31:0] addr
);
begin
S_AXI_ARADDR = addr;
S_AXI_ARVALID = 1'b1;
repeat(2) #STEP;
S_AXI_ARVALID = 1'b0;
S_AXI_RREADY = 1'b1;
#STEP;
S_AXI_RREADY = 1'b0;
#STEP;
end
endtask
// initialize
initial
begin
// initialize
S_AXI_ACLK = 1'b1;
S_AXI_ARESETN = 1;
#STEP;
// reset
S_AXI_ARESETN = 0;
#STEP;
S_AXI_ARESETN = 1;
#STEP;
repeat(2) #STEP;
fake_axi_master_write(32'h60000000, 32'd1);
fake_axi_master_write(32'h60000004, 32'd2);
fake_axi_master_read(32'h60000008);
end
endmodule
シミュレーターで動作を確認
Flow Navigatorウィンドウ→Project Manager→Run Behavioral Simulationボタンをクリックします。
シミュレーションに使用するトップモジュールを指定します。「...」ボタンをクリックします。
「hello_axi_testbench」を選択して「OK」をクリックします。
「Launch」をクリックします。
HDLシミュレーター「ISim」が起動します。回路をシミュレートして信号の波形やレジスタの値などを表示することができます。左側のオブジェクトパネルからポートやレジスタ名を波形ビューワにドラッグ&ドロップしたり並び替えたりしてレイアウトすることができます。
デフォルトでは数値は2進表示ですが、波形ビューワのオブジェクト名を右クリックして Radix → Hexadecimal を選択すると16進表示できます。
ここではあらかじめレイアウトした設定ファイルを開きます。
「Open」ボタンをクリックするとファイル選択画面が出るので
~/parallella_hw_projects/hello_axi/hello_axi_testbench/hello_axi_testbench.wcfg を開きます。
画面の右上の「Re-launch」ボタンをクリックし、「+」「-」のズームアイコンをクリックして表示幅を調整するとこのような画面になります。
この回路ではアドレス0x60000000にWriteするとレジスタAに値が入り、アドレス0x60000004でレジスタBに入り、そしてアドレス0x60000008をReadするとレジスタAとレジスタBの和が返されるようになっています。
S_AXI_AWADDRが書き込みアドレス、S_AXI_WDATAが書き込みデータ、S_AXI_RDATAが読み込みデータを表しているので、この波形からレジスタAに1がセットされ、レジスタBに2がセットされ、レジスタAとレジスタBの和である3が返されていることが分かります。
このテスト用の信号を出力しているのが hello_axi_testbench.v です。これは擬似的にAXI Masterデバイスを模倣してIPに信号を送っています。
HDLソースコードを修正した後、「Re-launch」をクリックすると即座にシミュレートされます。
開発の流れとしては、ロジック本体とテストベンチのソースコードを書き、ISimでシミュレートして動作の検証をした後、ビットストリームを生成して実機で動作させるという手順になります。(ビットストリームの生成には2〜30分かかるので、あまり頻繁には行えません。)
メニュー→File→ExitでISimを終了してビットストリームを生成します。
ビットストリームの生成
PlanAheadの「Sources」ウィンドウでDesign Sources→parallella_z7_top→system_stubを展開し、「system_i-system(system.xmp)」を右クリックして「Reset Output Products」を選択します。
この画面の内容は状況によって変化しますが、「OK」でXPSプロジェクトの生成ファイルがリセットされます。この作業は特にIPのソースコードを修正した場合などに必要です。
次に、「ParallellaのFPGAビットストリームを生成する」と同様の手順でビットストリームの生成、parallella.bit.binへの変換を行います。この手順は省略します。
IPの論理合成時のログは、
プロジェクトディレクトリ/プロジェクト名.srcs/sources_1/edk/parallella_70x0_x/synthesis/system_hello_axi_0_wrapper_xst.srp に出力されています。このログファイルにはエラーメッセージ、使用スライス、最大動作周波数などが書かれています。
ビットストリームの転送
(標準のビットストリーム parallella.bit.bin はあらかじめバックアップを取っておいてください。)
ネットワーク経由でビットストリームの転送を行うために以下のようなスクリプトを作っておくと便利です。
PCからParallellaにビットストリームを転送するスクリプト
PCの ~/parallella_hw_projects/プロジェクトのディレクトリ/ 以下に置きます。
以降、「192.168.0.123」の部分はParallellaのIPアドレスに置き換えてください。(ParallellaのIPアドレスはルータの管理画面などから確認してください。)
scpbitbin.sh
#!/bin/sh
scp bitbin/parallella.bit.bin linaro@192.168.0.123:
実行方法:
sh scpbitbin.sh
実行すると parallella.bit.bin がParallellaのホームディレクトリに転送されます。
PCからParallellaにsshでログインするスクリプト
sshparallella.sh
#!/bin/sh
ssh linaro@192.168.0.123
実行方法:
sh sshparallella.sh
Parallellaで parallella.bit.bin をSDカードのBOOTパーティションにコピーするスクリプト
Parallellaのホームディレクトリに置きます。
copybin.sh
#!/bin/sh
sudo mount /dev/mmcblk0p1 /mnt
sudo cp parallella.bit.bin /mnt/
sudo sync
sudo umount /mnt
実行方法:Parallellaにsshでログインして、
sh copybin.sh
そして
sudo reboot
すると新しいビットストリームが読み込まれます。
ドライバの展開、ビルド、実行
ドライバのtarballをParallellaに転送します。
cd ~/parallella_hw_projects/hello_axi
scp hello_axi_driver.tar.gz linaro@192.168.0.123:
ssh linaro@192.168.0.123
(以降Parallellaで)
tar xzf hello_axi_driver.tar.gz
cd hello_axi_driver
make
sudo ./main
ドライバと言っていますが実際はデバイスドライバは書かずに /dev/mem デバイスを使ってアプリケーションからMMIOでIPを直接制御しています。
通常、OSの上で動くアプリケーションは仮想アドレス空間にしかアクセスできませんが、/dev/mem を使うとアプリケーションから物理アドレス空間に簡単にアクセスすることができます。実行時にroot権限が必要なので通常のアプリケーションでは使用されませんが、開発や実験の際には便利に使えます。
main.c
/*
Copyright (c) 2014, miya
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdint.h>
#include <time.h>
#define DEVMEM "/dev/mem"
#define BASE_ADDR 0x60000000
#define REG_LEN 8
uint32_t input_num(const char *str)
{
printf("%s: ", str);
fflush(stdin);
uint32_t a;
if (scanf("%d", &a) != 1)
{
perror("input error");
abort();
}
return a;
}
int main(int argc, char *argv[])
{
int fd = open(DEVMEM, O_RDWR | O_SYNC);
if (fd == -1)
{
perror("cannot open /dev/mem");
abort();
}
off_t phy_addr = BASE_ADDR;
size_t length = 4 * REG_LEN;
uint32_t *mem = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, phy_addr);
if (mem == MAP_FAILED)
{
perror("map failed");
abort();
}
while (1)
{
volatile uint32_t *reg = mem;
*(reg + 0) = input_num("a");
*(reg + 1) = input_num("b");
printf("a + b = %d\n", *(reg + 2));
}
munmap(mem, length);
close(fd);
return 0;
}