├── .github └── workflows │ └── build.yml ├── LICENSE ├── README.md ├── bus_if ├── axi4_lite.h ├── bus_axi4_lite.cpp └── bus_axi4_lite.h ├── cli ├── main.cpp └── main_riscv_linux.cpp ├── core ├── bin_load.cpp ├── bin_load.h ├── console.cpp ├── console.h ├── console_io.h ├── cpu.cpp ├── cpu.h ├── device.h ├── elf_load.cpp ├── elf_load.h ├── mem_api.h ├── memory.h └── syscall_if.h ├── cpu-armv6m ├── armv6m.cpp ├── armv6m.h └── armv6m_opcodes.h ├── cpu-mips-i ├── mips.cpp ├── mips.h └── mips_isa.h ├── cpu-rv32 ├── rv32.cpp ├── rv32.h └── rv32_isa.h ├── cpu-rv64 ├── rv64.cpp ├── rv64.h └── rv64_isa.h ├── device-tree ├── device_tree.cpp └── device_tree.h ├── display ├── display.cpp └── display.h ├── docs └── screenshot.png ├── makefile ├── net ├── net_device.h ├── net_tap.cpp └── net_tap.h ├── peripherals ├── device_dummy.h ├── device_frame_buffer.h ├── device_irq_ctrl.h ├── device_irq_plic.h ├── device_spi_lite.h ├── device_systick.h ├── device_sysuart.h ├── device_timer_clint.h ├── device_timer_owl.h ├── device_timer_r5.h ├── device_uart_8250.h └── device_uart_lite.h ├── platforms ├── platform.h ├── platform_basic.h ├── platform_cpu.h ├── platform_device_tree.h └── platform_virt.h ├── sbi ├── sbi.cpp └── sbi.h └── virtio ├── virtio.cpp ├── virtio.h ├── virtio_block.cpp ├── virtio_block.h ├── virtio_net.cpp └── virtio_net.h /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | 7 | build_linux: 8 | runs-on: ubuntu-18.04 9 | steps: 10 | - uses: actions/checkout@v2 11 | - run: sudo apt-get install libelf-dev binutils-dev libfdt-dev 12 | - run: make 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2019, ultraembedded 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExactStep - Instruction Accurate Instruction Set Simulator 2 | 3 | ![Build](https://github.com/ultraembedded/exactstep/workflows/Build/badge.svg) 4 | 5 | Github: https://github.com/ultraembedded/exactstep 6 | 7 | ExactStep is a simple multi-target instruction set simulator supporting RISC-V (RV32IMAC, RV64IMAC), MIPS (mips-i), and ARM-v6m (with others to come soon). 8 | The emphasis of this project is on ease of extension, allowing its use as a library for cosimulation, peripheral development and System-C bus interfacing, rather than on raw execution performance. 9 | 10 | Unlike QEMU and other CPU emulators which make use of dynamic binary translation, ExactStep executes one instruction per call to **cpu::step()**. 11 | 12 | ![](docs/screenshot.png) 13 | 14 | ## Cloning 15 | ``` 16 | git clone https://github.com/ultraembedded/exactstep.git 17 | ``` 18 | 19 | ## Building 20 | 21 | This project uses make and ELF, BFD, FDT libraries. 22 | 23 | If you are using a Debian based Linux distro (Ubuntu / Linux Mint), you can install the required dependencies using; 24 | 25 | ``` 26 | sudo apt-get install libelf-dev binutils-dev libfdt-dev 27 | ``` 28 | 29 | To build the default command line simulator and RISC-V Linux simulators; 30 | ``` 31 | cd exactstep 32 | make 33 | ``` 34 | 35 | ## ExactStep: Usage 36 | *exactstep* supports various emulated CPU architectures (RISC-V, MIPS, ARM), and is used to run bare-metal executables compiled for those ISAs; 37 | ``` 38 | ./exactstep 39 | Usage: 40 | --elf | -f FILE File to load (ELF or BIN) 41 | --march | -m MISA Machine variant (e.g. RV32IMAC, RV64I, armv6, mips, ...) 42 | --platform | -P PLATFORM Platform to simulate (basic|virt) 43 | --dtb | -D FILE Device tree blob (binary) 44 | --trace | -t 1/0 Enable instruction trace 45 | --trace-mask | -v 0xXX Trace mask (verbosity level) 46 | --cycles | -c NUM Max instructions to execute 47 | --stop-pc | -r PC Stop at PC address 48 | --trace-pc | -e PC Trace from PC address 49 | --elf-phys | -E Load to ELF section to physical addresses (suitable for bootloaders) 50 | --mem-base | -b VAL Memory base address (for binary loads) 51 | --mem-size | -s VAL Memory size (for binary loads) 52 | --dump-file | -p FILE File to dump memory contents to after completion 53 | --dump-start | -j SYM/A Symbol name for memory dump start (or 0xADDR) 54 | --dump-end | -k SYM/A Symbol name for memory dump end (or 0xADDR) 55 | --dump-reg-f | -R FILE File to dump register file contents to after completion 56 | --dump-reg-s | -S NUM Number of register file entries to dump 57 | --vda | -V FILE Disk image for VirtIO block device (/dev/vda) 58 | --tap | -T TAP Tap device for VirtIO net device 59 | ``` 60 | 61 | The default architecture is a RV32IMAC CPU model. To run a basic ELF; 62 | ```sh 63 | ./exactstep -f your_elf.elf 64 | ``` 65 | 66 | ## Exactstep-riscv-linux: Usage 67 | *exactstep-riscv-linux* is a RISC-V (32-bit or 64-bit) specific simulator which contains a built-in SBI (Supervisor Binary Interface) implementation that enables booting RISC-V Linux kernels compiled for supervisor mode. 68 | Root filesystems can also be provided by initrd, VirtIO block device, or VirtIO network (nfs) boot. 69 | 70 | ``` 71 | ./exactstep-riscv-linux 72 | Usage: 73 | --elf | -f FILE File to load (ELF) 74 | --bin | -b FILE File to load (binary) 75 | --march | -m MISA Machine variant (e.g. RV32IMAC, RV64I, ...) 76 | --platform | -P PLATFORM Platform to simulate (basic|virt) 77 | --dtb | -D FILE Device tree blob (binary) 78 | --dtb-base | -B 0xaddr Device tree blob load address 79 | --trace | -t 1/0 Enable instruction trace 80 | --trace-mask | -v 0xXX Trace mask (verbosity level) 81 | --cycles | -c NUM Max instructions to execute 82 | --stop-pc | -r PC Stop at PC address 83 | --trace-pc | -e PC Trace from PC address 84 | --vda | -V FILE Disk image for VirtIO block device (/dev/vda) 85 | --tap | -T TAP Tap device for VirtIO net device 86 | --initrd | -i FILE initrd binary (optional) 87 | ``` 88 | 89 | Example usage (with a device tree compiled to a DTB file using the Linux Kernel dtc util); 90 | ```sh 91 | ./exactstep-riscv-linux --elf ./vmlinux-rv32ima-5.0 --dtb ./config.dtb --initrd ./initrd.cpio 92 | ``` 93 | 94 | ## Running RISC-V Compliance Tests 95 | 96 | ExactStep passes the RISC-V Compliance Tests for the rv32i, rv32im, rv32imc, rv64i, rv64im categories; 97 | ``` 98 | # Get compliance test suite 99 | git clone https://github.com/ultraembedded/riscv-compliance.git 100 | cd riscv-compliance 101 | 102 | # Set path to built exactstep executable 103 | export TARGET_SIM=/path/to/github/exactstep/exactstep 104 | 105 | # Run test suite 106 | make RISCV_TARGET=exactstep 107 | ``` 108 | 109 | ## Running RISC-V Linux 110 | ``` 111 | # Get prebuilt bootloader + kernel + rootfs images 112 | git clone https://github.com/ultraembedded/riscv-linux-prebuilt.git 113 | cd riscv-linux-prebuilt 114 | 115 | # Boot Linux (with Busybox userspace) 116 | exactstep --march RV64IMAC --elf opensbi-kernel-busybox/qemu-virt-rv64-5.4-rc7-busybox-1.32.0.elf --dtb opensbi-kernel-busybox/qemu-virt-rv64-config.dtb 117 | ``` 118 | 119 | ## License 120 | 121 | [BSD 3-Clause](LICENSE) 122 | -------------------------------------------------------------------------------- /bus_if/axi4_lite.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef AXI4_LITE_H 9 | #define AXI4_LITE_H 10 | 11 | #include 12 | 13 | //---------------------------------------------------------------- 14 | // Interface (master) 15 | //---------------------------------------------------------------- 16 | class axi4_lite_master 17 | { 18 | public: 19 | // Members 20 | sc_uint <1> AWVALID; 21 | sc_uint <32> AWADDR; 22 | sc_uint <1> WVALID; 23 | sc_uint <32> WDATA; 24 | sc_uint <4> WSTRB; 25 | sc_uint <1> BREADY; 26 | sc_uint <1> ARVALID; 27 | sc_uint <32> ARADDR; 28 | sc_uint <1> RREADY; 29 | 30 | // Construction 31 | axi4_lite_master() { init(); } 32 | 33 | void init(void) 34 | { 35 | AWVALID = 0; 36 | AWADDR = 0; 37 | WVALID = 0; 38 | WDATA = 0; 39 | WSTRB = 0; 40 | BREADY = 0; 41 | ARVALID = 0; 42 | ARADDR = 0; 43 | RREADY = 0; 44 | } 45 | 46 | bool operator == (const axi4_lite_master & v) const 47 | { 48 | bool eq = true; 49 | eq &= (AWVALID == v.AWVALID); 50 | eq &= (AWADDR == v.AWADDR); 51 | eq &= (WVALID == v.WVALID); 52 | eq &= (WDATA == v.WDATA); 53 | eq &= (WSTRB == v.WSTRB); 54 | eq &= (BREADY == v.BREADY); 55 | eq &= (ARVALID == v.ARVALID); 56 | eq &= (ARADDR == v.ARADDR); 57 | eq &= (RREADY == v.RREADY); 58 | return eq; 59 | } 60 | 61 | friend void sc_trace(sc_trace_file *tf, const axi4_lite_master & v, const std::string & path) 62 | { 63 | sc_trace(tf,v.AWVALID, path + "/awvalid"); 64 | sc_trace(tf,v.AWADDR, path + "/awaddr"); 65 | sc_trace(tf,v.WVALID, path + "/wvalid"); 66 | sc_trace(tf,v.WDATA, path + "/wdata"); 67 | sc_trace(tf,v.WSTRB, path + "/wstrb"); 68 | sc_trace(tf,v.BREADY, path + "/bready"); 69 | sc_trace(tf,v.ARVALID, path + "/arvalid"); 70 | sc_trace(tf,v.ARADDR, path + "/araddr"); 71 | sc_trace(tf,v.RREADY, path + "/rready"); 72 | } 73 | 74 | friend ostream& operator << (ostream& os, axi4_lite_master const & v) 75 | { 76 | os << hex << "AWVALID: " << v.AWVALID << " "; 77 | os << hex << "AWADDR: " << v.AWADDR << " "; 78 | os << hex << "WVALID: " << v.WVALID << " "; 79 | os << hex << "WDATA: " << v.WDATA << " "; 80 | os << hex << "WSTRB: " << v.WSTRB << " "; 81 | os << hex << "BREADY: " << v.BREADY << " "; 82 | os << hex << "ARVALID: " << v.ARVALID << " "; 83 | os << hex << "ARADDR: " << v.ARADDR << " "; 84 | os << hex << "RREADY: " << v.RREADY << " "; 85 | return os; 86 | } 87 | 88 | friend istream& operator >> ( istream& is, axi4_lite_master & val) 89 | { 90 | // Not implemented 91 | return is; 92 | } 93 | }; 94 | 95 | #define MEMBER_COPY_AXI4_LITE_MASTER(s,d) do { \ 96 | s.AWVALID = d.AWVALID; \ 97 | s.AWADDR = d.AWADDR; \ 98 | s.WVALID = d.WVALID; \ 99 | s.WDATA = d.WDATA; \ 100 | s.WSTRB = d.WSTRB; \ 101 | s.BREADY = d.BREADY; \ 102 | s.ARVALID = d.ARVALID; \ 103 | s.ARADDR = d.ARADDR; \ 104 | s.RREADY = d.RREADY; \ 105 | } while (0) 106 | 107 | //---------------------------------------------------------------- 108 | // Interface (slave) 109 | //---------------------------------------------------------------- 110 | class axi4_lite_slave 111 | { 112 | public: 113 | // Members 114 | sc_uint <1> AWREADY; 115 | sc_uint <1> WREADY; 116 | sc_uint <1> BVALID; 117 | sc_uint <2> BRESP; 118 | sc_uint <1> ARREADY; 119 | sc_uint <1> RVALID; 120 | sc_uint <32> RDATA; 121 | sc_uint <2> RRESP; 122 | 123 | // Construction 124 | axi4_lite_slave() { init(); } 125 | 126 | void init(void) 127 | { 128 | AWREADY = 0; 129 | WREADY = 0; 130 | BVALID = 0; 131 | BRESP = 0; 132 | ARREADY = 0; 133 | RVALID = 0; 134 | RDATA = 0; 135 | RRESP = 0; 136 | } 137 | 138 | bool operator == (const axi4_lite_slave & v) const 139 | { 140 | bool eq = true; 141 | eq &= (AWREADY == v.AWREADY); 142 | eq &= (WREADY == v.WREADY); 143 | eq &= (BVALID == v.BVALID); 144 | eq &= (BRESP == v.BRESP); 145 | eq &= (ARREADY == v.ARREADY); 146 | eq &= (RVALID == v.RVALID); 147 | eq &= (RDATA == v.RDATA); 148 | eq &= (RRESP == v.RRESP); 149 | return eq; 150 | } 151 | 152 | friend void sc_trace(sc_trace_file *tf, const axi4_lite_slave & v, const std::string & path) 153 | { 154 | sc_trace(tf,v.AWREADY, path + "/awready"); 155 | sc_trace(tf,v.WREADY, path + "/wready"); 156 | sc_trace(tf,v.BVALID, path + "/bvalid"); 157 | sc_trace(tf,v.BRESP, path + "/bresp"); 158 | sc_trace(tf,v.ARREADY, path + "/arready"); 159 | sc_trace(tf,v.RVALID, path + "/rvalid"); 160 | sc_trace(tf,v.RDATA, path + "/rdata"); 161 | sc_trace(tf,v.RRESP, path + "/rresp"); 162 | } 163 | 164 | friend ostream& operator << (ostream& os, axi4_lite_slave const & v) 165 | { 166 | os << hex << "AWREADY: " << v.AWREADY << " "; 167 | os << hex << "WREADY: " << v.WREADY << " "; 168 | os << hex << "BVALID: " << v.BVALID << " "; 169 | os << hex << "BRESP: " << v.BRESP << " "; 170 | os << hex << "ARREADY: " << v.ARREADY << " "; 171 | os << hex << "RVALID: " << v.RVALID << " "; 172 | os << hex << "RDATA: " << v.RDATA << " "; 173 | os << hex << "RRESP: " << v.RRESP << " "; 174 | return os; 175 | } 176 | 177 | friend istream& operator >> ( istream& is, axi4_lite_slave & val) 178 | { 179 | // Not implemented 180 | return is; 181 | } 182 | }; 183 | 184 | #define MEMBER_COPY_AXI4_LITE_SLAVE(s,d) do { \ 185 | s.AWREADY = d.AWREADY; \ 186 | s.WREADY = d.WREADY; \ 187 | s.BVALID = d.BVALID; \ 188 | s.BRESP = d.BRESP; \ 189 | s.ARREADY = d.ARREADY; \ 190 | s.RVALID = d.RVALID; \ 191 | s.RDATA = d.RDATA; \ 192 | s.RRESP = d.RRESP; \ 193 | } while (0) 194 | 195 | 196 | #endif 197 | -------------------------------------------------------------------------------- /bus_if/bus_axi4_lite.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include "bus_axi4_lite.h" 9 | 10 | //----------------------------------------------------------------- 11 | // Construction 12 | //----------------------------------------------------------------- 13 | bus_axi4_lite::bus_axi4_lite(sc_module_name name, uint32_t base, uint32_t size, device *irq_ctrl, int irq_num): 14 | sc_module(name), 15 | device("bus_axi4", base, size, irq_ctrl, irq_num) 16 | { 17 | SC_CTHREAD(monitor_irq, clk_in.pos()); 18 | } 19 | //----------------------------------------------------------------- 20 | // monitor_irq: Monitor IRQ line and translate to func call 21 | //----------------------------------------------------------------- 22 | void bus_axi4_lite::monitor_irq(void) 23 | { 24 | while (true) 25 | { 26 | if (rst_in.read()) 27 | ; 28 | else if (intr_in.read()) 29 | raise_interrupt(); 30 | 31 | wait(); 32 | } 33 | } 34 | //----------------------------------------------------------------- 35 | // write32 36 | //----------------------------------------------------------------- 37 | bool bus_axi4_lite::write32(uint32_t address, uint32_t data) 38 | { 39 | axi4_lite_master axi_o; 40 | axi4_lite_slave axi_i; 41 | 42 | axi_o.AWVALID = true; 43 | axi_o.AWADDR = address; 44 | axi_o.WVALID = true; 45 | axi_o.WDATA = data; 46 | axi_o.WSTRB = 0xF; 47 | axi_o.BREADY = true; 48 | 49 | axi_out.write(axi_o); 50 | 51 | // Wait for accept 52 | do 53 | { 54 | wait(); 55 | axi_i = axi_in.read(); 56 | 57 | if (axi_i.AWREADY) 58 | { 59 | axi_o.AWVALID = false; 60 | axi_o.AWADDR = 0; 61 | } 62 | 63 | if (axi_i.WREADY) 64 | { 65 | axi_o.WVALID = false; 66 | axi_o.WDATA = 0; 67 | axi_o.WSTRB = 0; 68 | } 69 | 70 | axi_out.write(axi_o); 71 | } 72 | while (axi_o.AWVALID || axi_o.WVALID); 73 | 74 | axi_o.BREADY = true; 75 | axi_out.write(axi_o); 76 | 77 | while (!axi_i.BVALID) 78 | { 79 | wait(); 80 | axi_i = axi_in.read(); 81 | } 82 | 83 | return axi_i.BRESP == 0; 84 | } 85 | //----------------------------------------------------------------- 86 | // read32 87 | //----------------------------------------------------------------- 88 | bool bus_axi4_lite::read32(uint32_t address, uint32_t &data) 89 | { 90 | axi4_lite_master axi_o; 91 | axi4_lite_slave axi_i; 92 | 93 | axi_o.ARVALID = true; 94 | axi_o.ARADDR = address; 95 | axi_o.RREADY = true; 96 | 97 | axi_out.write(axi_o); 98 | 99 | // Wait for accept 100 | do 101 | { 102 | wait(); 103 | axi_i = axi_in.read(); 104 | } 105 | while (!axi_i.ARREADY); 106 | 107 | axi_o.init(); 108 | axi_o.RREADY = true; 109 | axi_out.write(axi_o); 110 | 111 | while (!axi_i.RVALID) 112 | { 113 | wait(); 114 | axi_i = axi_in.read(); 115 | } 116 | data = axi_i.RDATA; 117 | return axi_i.RRESP == 0; 118 | } 119 | -------------------------------------------------------------------------------- /bus_if/bus_axi4_lite.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef BUS_AXI4_LITE_H 9 | #define BUS_AXI4_LITE_H 10 | 11 | #include "axi4_lite.h" 12 | #include "memory.h" 13 | 14 | //------------------------------------------------------------- 15 | // bus_axi4_lite: AXI4 lite driver interface 16 | //------------------------------------------------------------- 17 | class bus_axi4_lite: public sc_module, public device 18 | { 19 | public: 20 | //------------------------------------------------------------- 21 | // Interface I/O 22 | //------------------------------------------------------------- 23 | sc_in clk_in; 24 | sc_in rst_in; 25 | 26 | sc_out axi_out; 27 | sc_in axi_in; 28 | 29 | sc_in intr_in; 30 | 31 | //------------------------------------------------------------- 32 | // Constructor 33 | //------------------------------------------------------------- 34 | SC_HAS_PROCESS(bus_axi4_lite); 35 | bus_axi4_lite(sc_module_name name, uint32_t base, uint32_t size, device *irq_ctrl, int irq_num); 36 | 37 | //------------------------------------------------------------- 38 | // Trace 39 | //------------------------------------------------------------- 40 | void add_trace(sc_trace_file *vcd, std::string prefix) 41 | { 42 | #undef TRACE_SIGNAL 43 | #define TRACE_SIGNAL(s) sc_trace(vcd,s,prefix + #s) 44 | 45 | TRACE_SIGNAL(axi_out); 46 | TRACE_SIGNAL(axi_in); 47 | 48 | #undef TRACE_SIGNAL 49 | } 50 | 51 | bool write32(uint32_t address, uint32_t data); 52 | bool read32(uint32_t address, uint32_t &data); 53 | int clock(void) 54 | { 55 | return 0; 56 | } 57 | 58 | void monitor_irq(void); 59 | 60 | protected: 61 | //------------------------------------------------------------- 62 | // Members 63 | //------------------------------------------------------------- 64 | }; 65 | 66 | #endif -------------------------------------------------------------------------------- /cli/main_riscv_linux.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "console.h" 17 | #include "elf_load.h" 18 | #include "bin_load.h" 19 | 20 | #include "platform_device_tree.h" 21 | #include "sbi.h" 22 | 23 | #include "virtio_block.h" 24 | #include "virtio_net.h" 25 | 26 | static volatile bool m_user_abort = false; 27 | 28 | //----------------------------------------------------------------- 29 | // Command line options 30 | //----------------------------------------------------------------- 31 | #define GETOPTS_ARGS "t:v:r:f:D:B:m:c:e:V:T:i:b:h" 32 | 33 | static struct option long_options[] = 34 | { 35 | {"trace", required_argument, 0, 't'}, 36 | {"trace-mask", required_argument, 0, 'v'}, 37 | {"stop-pc", required_argument, 0, 'r'}, 38 | {"elf", required_argument, 0, 'f'}, 39 | {"bin", required_argument, 0, 'b'}, 40 | {"dtb", required_argument, 0, 'D'}, 41 | {"dtb-base", required_argument, 0, 'B'}, 42 | {"march", required_argument, 0, 'm'}, 43 | {"cycles", required_argument, 0, 'c'}, 44 | {"trace-pc", required_argument, 0, 'e'}, 45 | {"vda", required_argument, 0, 'V'}, 46 | {"tap", required_argument, 0, 'T'}, 47 | {"initrd", required_argument, 0, 'i'}, 48 | {"help", no_argument, 0, 'h'}, 49 | {0, 0, 0, 0} 50 | }; 51 | 52 | static void help_options(void) 53 | { 54 | fprintf (stderr,"Usage:\n"); 55 | fprintf (stderr," --elf | -f FILE File to load (ELF)\n"); 56 | fprintf (stderr," --bin | -b FILE File to load (binary)\n"); 57 | fprintf (stderr," --march | -m MISA Machine variant (e.g. RV32IMAC, RV64I, ...)\n"); 58 | fprintf (stderr," --platform | -P PLATFORM Platform to simulate (basic|virt)\n"); 59 | fprintf (stderr," --dtb | -D FILE Device tree blob (binary)\n"); 60 | fprintf (stderr," --dtb-base | -B 0xaddr Device tree blob load address\n"); 61 | fprintf (stderr," --trace | -t 1/0 Enable instruction trace\n"); 62 | fprintf (stderr," --trace-mask | -v 0xXX Trace mask (verbosity level)\n"); 63 | fprintf (stderr," --cycles | -c NUM Max instructions to execute\n"); 64 | fprintf (stderr," --stop-pc | -r PC Stop at PC address\n"); 65 | fprintf (stderr," --trace-pc | -e PC Trace from PC address\n"); 66 | fprintf (stderr," --vda | -V FILE Disk image for VirtIO block device (/dev/vda)\n"); 67 | fprintf (stderr," --tap | -T TAP Tap device for VirtIO net device\n"); 68 | fprintf (stderr," --initrd | -i FILE initrd binary (optional)\n"); 69 | exit(-1); 70 | } 71 | //----------------------------------------------------------------- 72 | // sigint_handler: Abort execution 73 | //----------------------------------------------------------------- 74 | static void sigint_handler(int s) 75 | { 76 | if (m_user_abort) 77 | { 78 | printf("Aborted\n"); 79 | exit(0); 80 | } 81 | m_user_abort = true; 82 | } 83 | //----------------------------------------------------------------- 84 | // main 85 | //----------------------------------------------------------------- 86 | int main(int argc, char *argv[]) 87 | { 88 | uint64_t cycles = 0; 89 | int64_t max_cycles = (int64_t)-1; 90 | const char * filename = NULL; 91 | bool is_binary = false; 92 | const char * march = NULL; 93 | int help = 0; 94 | int trace = 0; 95 | uint32_t trace_mask = 1; 96 | uint32_t stop_pc = 0xFFFFFFFF; 97 | uint32_t trace_pc = 0xFFFFFFFF; 98 | uint32_t dtb_base_user = 0xFFFFFFFF; 99 | const char * device_blob = NULL; 100 | const char * platform_name = NULL; 101 | const char * vda_file = NULL; 102 | const char * tap_device = NULL; 103 | const char * initrd_filename= NULL; 104 | int c; 105 | 106 | int option_index = 0; 107 | while ((c = getopt_long (argc, argv, GETOPTS_ARGS, long_options, &option_index)) != -1) 108 | { 109 | switch(c) 110 | { 111 | case 't': 112 | trace = strtoul(optarg, NULL, 0); 113 | break; 114 | case 'v': 115 | trace_mask = strtoul(optarg, NULL, 0); 116 | break; 117 | case 'r': 118 | stop_pc = strtoul(optarg, NULL, 0); 119 | break; 120 | case 'f': 121 | filename = optarg; 122 | is_binary = false; 123 | break; 124 | case 'b': 125 | filename = optarg; 126 | is_binary = true; 127 | break; 128 | case 'm': 129 | march = optarg; 130 | break; 131 | case 'D': 132 | device_blob = optarg; 133 | break; 134 | case 'B': 135 | dtb_base_user = strtoul(optarg, NULL, 0); 136 | break; 137 | case 'c': 138 | max_cycles = (int64_t)strtoull(optarg, NULL, 0); 139 | break; 140 | case 'e': 141 | trace_pc = strtoul(optarg, NULL, 0); 142 | break; 143 | case 'V': 144 | vda_file = optarg; 145 | break; 146 | case 'T': 147 | tap_device = optarg; 148 | break; 149 | case 'i': 150 | initrd_filename = optarg; 151 | break; 152 | case '?': 153 | default: 154 | help = 1; 155 | break; 156 | } 157 | } 158 | 159 | if (help || (filename == NULL || device_blob == NULL)) 160 | help_options(); 161 | 162 | console_io *con = new console(); 163 | 164 | if (!march) 165 | march = "RV32IMAC"; 166 | 167 | // Device tree blob specified SoC 168 | platform_device_tree * plat = new platform_device_tree(march, device_blob, con); 169 | cpu *sim = plat->get_cpu(); 170 | if (!sim) 171 | return -1; 172 | sim->set_console(con); 173 | 174 | // Get memory 175 | uint32_t mem_base = plat->get_mem_base(); 176 | uint32_t mem_size = plat->get_mem_size(); 177 | 178 | // Create some extra space for the DTB 179 | uint32_t dtb_base = (dtb_base_user != 0xFFFFFFFF) ? dtb_base_user : ((mem_base + mem_size + 4096) & ~(4096-1)); 180 | uint32_t dtb_size = (64 * 1024); 181 | 182 | printf("MEM: Create memory 0x%08x-%08x\n", mem_base, mem_base + mem_size-1); 183 | if (!sim->create_memory(mem_base, mem_size)) 184 | { 185 | fprintf (stderr,"Error: Could not create memory\n"); 186 | return -1; 187 | } 188 | 189 | printf("MEM: Create memory 0x%08x-%08x [DTB]\n", dtb_base, dtb_base + dtb_size-1); 190 | if (!sim->create_memory(dtb_base, dtb_size)) 191 | { 192 | fprintf (stderr,"Error: Could not create memory\n"); 193 | return -1; 194 | } 195 | 196 | // Load kernel 197 | const char *ext = filename ? strrchr(filename, '.') : NULL; 198 | bool is_bin = is_binary || (ext && !strcmp(ext, ".bin")); 199 | 200 | // Binary 201 | if (is_bin) 202 | { 203 | bin_load bin(filename, sim); 204 | if (!bin.load(mem_base)) 205 | { 206 | fprintf (stderr,"Error: Could not open %s\n", filename); 207 | return -1; 208 | } 209 | } 210 | // ELF 211 | else 212 | { 213 | int64_t load_offset; 214 | 215 | // RV64 216 | if (sim->get_reg_width() == 64) 217 | load_offset = 0xffffffe000000000 - mem_base; 218 | // RV32 219 | else 220 | load_offset = 0xC0000000 - mem_base; 221 | 222 | elf_load elf(filename, sim, false, -load_offset); 223 | if (!elf.load()) 224 | { 225 | fprintf (stderr,"Error: Could not open %s\n", filename); 226 | return -1; 227 | } 228 | } 229 | 230 | // Optional initrd 231 | if (initrd_filename) 232 | { 233 | uint32_t initrd_base = plat->get_initrd_base(); 234 | uint32_t initrd_size = plat->get_initrd_size(); 235 | 236 | if (initrd_base == 0 || initrd_size == 0) 237 | { 238 | fprintf (stderr,"Error: linux,initrd-start / linux,initrd-end not specified\n"); 239 | return -1; 240 | } 241 | else 242 | { 243 | printf("Loading initrd to 0x%08x-0x%08x\n", initrd_base, initrd_base + initrd_size); 244 | bin_load bin(initrd_filename, sim); 245 | if (!bin.load(initrd_base)) 246 | { 247 | fprintf (stderr,"Error: Could not open %s\n", initrd_filename); 248 | return -1; 249 | } 250 | } 251 | } 252 | 253 | // Load device tree blob 254 | bin_load bin_dtb(device_blob, sim); 255 | if (!bin_dtb.load(dtb_base)) 256 | { 257 | fprintf (stderr,"Error: Could not open %s\n", device_blob); 258 | return -1; 259 | } 260 | 261 | // Setup SBI 262 | sim->reset(mem_base); 263 | sbi::setup(sim, con, mem_base, dtb_base); 264 | 265 | // User specified virtio block device file 266 | int vda_idx = 0; 267 | if (vda_file) 268 | { 269 | virtio * vda_dev = (virtio *)sim->find_device("virtio", vda_idx++); 270 | if (vda_dev) 271 | { 272 | virtio_block *vda_blk_dev = new virtio_block(vda_dev); 273 | if (!vda_blk_dev->open(vda_file)) 274 | { 275 | fprintf (stderr,"Error: Could not open %s\n", vda_file); 276 | return -1; 277 | } 278 | } 279 | } 280 | 281 | // User specified tap device for virtio networking 282 | #ifdef INCLUDE_NET_DEVICE 283 | if (tap_device) 284 | { 285 | virtio * vda_dev = (virtio *)sim->find_device("virtio", vda_idx++); 286 | if (vda_dev) 287 | { 288 | virtio_net *vda_net_dev = new virtio_net(vda_dev); 289 | if (!vda_net_dev->open(tap_device, NULL)) 290 | { 291 | fprintf (stderr,"Error: Could not open %s\n", tap_device); 292 | return -1; 293 | } 294 | } 295 | } 296 | #endif 297 | 298 | // Enable trace? 299 | if (trace) 300 | sim->enable_trace(trace_mask); 301 | 302 | cycles = 0; 303 | 304 | // Catch SIGINT to restore terminal settings on exit 305 | signal(SIGINT, sigint_handler); 306 | 307 | uint32_t current_pc = 0; 308 | while (!sim->get_fault() && !sim->get_stopped() && current_pc != stop_pc && !m_user_abort) 309 | { 310 | current_pc = sim->get_pc(); 311 | sim->step(); 312 | cycles++; 313 | 314 | if (max_cycles != (int64_t)-1 && max_cycles == cycles) 315 | break; 316 | 317 | // Turn trace on 318 | if (trace_pc == current_pc) 319 | sim->enable_trace(trace_mask); 320 | } 321 | 322 | // Fault occurred? 323 | if (sim->get_fault()) 324 | return 1; 325 | else 326 | { 327 | sim->stats_dump(); 328 | return 0; 329 | } 330 | } 331 | -------------------------------------------------------------------------------- /core/bin_load.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "bin_load.h" 15 | 16 | //-------------------------------------------------------------------- 17 | // Constructor 18 | //-------------------------------------------------------------------- 19 | bin_load::bin_load(const char *filename, mem_api *target) 20 | { 21 | m_filename = std::string(filename); 22 | m_target = target; 23 | } 24 | //----------------------------------------------------------------- 25 | // load: Binary load 26 | //----------------------------------------------------------------- 27 | bool bin_load::load(uint32_t mem_base, uint32_t mem_size) 28 | { 29 | // Load file 30 | FILE *f = fopen(m_filename.c_str(), "rb"); 31 | if (f) 32 | { 33 | long size; 34 | uint8_t *buf; 35 | int error = 1; 36 | 37 | // Get size 38 | fseek(f, 0, SEEK_END); 39 | size = ftell(f); 40 | rewind(f); 41 | 42 | buf = (uint8_t*)malloc(size); 43 | if (buf) 44 | { 45 | // Read file data in 46 | int len = fread(buf, 1, size, f); 47 | 48 | if (!m_target->create_memory(mem_base, mem_size)) 49 | fprintf (stderr,"Error: Could not allocate memory\n"); 50 | else 51 | { 52 | error = 0; 53 | for (int i=0;ivalid_addr(mem_base + i)) 56 | m_target->write(mem_base + i, buf[i]); 57 | else 58 | { 59 | fprintf (stderr,"Error: Could not load image to memory\n"); 60 | error = 1; 61 | break; 62 | } 63 | } 64 | } 65 | 66 | free(buf); 67 | fclose(f); 68 | } 69 | 70 | return !error; 71 | } 72 | else 73 | { 74 | fprintf (stderr,"Error: Could not open %s\n", m_filename.c_str()); 75 | return false; 76 | } 77 | } 78 | //----------------------------------------------------------------- 79 | // load: Binary load (memory assumed to already exist) 80 | //----------------------------------------------------------------- 81 | bool bin_load::load(uint32_t mem_base) 82 | { 83 | // Load file 84 | FILE *f = fopen(m_filename.c_str(), "rb"); 85 | if (f) 86 | { 87 | long size; 88 | uint8_t *buf; 89 | int error = 1; 90 | 91 | // Get size 92 | fseek(f, 0, SEEK_END); 93 | size = ftell(f); 94 | rewind(f); 95 | 96 | buf = (uint8_t*)malloc(size); 97 | if (buf) 98 | { 99 | // Read file data in 100 | int len = fread(buf, 1, size, f); 101 | 102 | error = 0; 103 | for (int i=0;ivalid_addr(mem_base + i)) 106 | m_target->write(mem_base + i, buf[i]); 107 | else 108 | { 109 | fprintf (stderr,"Error: Could not load image to memory\n"); 110 | error = 1; 111 | break; 112 | } 113 | } 114 | 115 | free(buf); 116 | fclose(f); 117 | } 118 | 119 | return !error; 120 | } 121 | else 122 | { 123 | fprintf (stderr,"Error: Could not open %s\n", m_filename.c_str()); 124 | return false; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /core/bin_load.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __BIN_LOAD_H__ 9 | #define __BIN_LOAD_H__ 10 | 11 | #include "mem_api.h" 12 | #include 13 | 14 | //-------------------------------------------------------------------- 15 | // Binary loader 16 | //-------------------------------------------------------------------- 17 | class bin_load 18 | { 19 | public: 20 | bin_load(const char *filename, mem_api *target); 21 | 22 | bool load(uint32_t mem_base, uint32_t mem_size); 23 | bool load(uint32_t mem_base); 24 | 25 | protected: 26 | std::string m_filename; 27 | mem_api * m_target; 28 | }; 29 | 30 | #endif -------------------------------------------------------------------------------- /core/console.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "console.h" 18 | 19 | //----------------------------------------------------------------- 20 | // Locals 21 | //----------------------------------------------------------------- 22 | static struct sigaction _sigaction; 23 | static struct termios _term_settings; 24 | 25 | //----------------------------------------------------------------- 26 | // console_sigint_handler 27 | //----------------------------------------------------------------- 28 | static void console_sigint_handler(int s) 29 | { 30 | // Jump to exit handler! 31 | exit(1); 32 | } 33 | //----------------------------------------------------------------- 34 | // console_exit_handler 35 | //----------------------------------------------------------------- 36 | static void console_exit_handler(void) 37 | { 38 | // Restore original terminal settings 39 | tcsetattr(fileno(stdin), TCSANOW, &_term_settings); 40 | } 41 | //----------------------------------------------------------------- 42 | // console: Simple polled console 43 | //----------------------------------------------------------------- 44 | console::console() 45 | { 46 | struct termios term; 47 | 48 | // Backup terminal settings 49 | tcgetattr(fileno(stdin), &_term_settings); 50 | 51 | // Don't buffer character entry 52 | memcpy(&term, &_term_settings, sizeof(struct termios)); 53 | term.c_lflag &= ~(ECHO|ICANON); 54 | term.c_cc[VTIME] = 0; 55 | term.c_cc[VMIN] = 0; 56 | tcsetattr(fileno(stdin), TCSANOW, &term); 57 | 58 | // Catch SIGINT to restore terminal settings on exit 59 | signal(SIGINT, console_sigint_handler); 60 | 61 | // Register exit() handler 62 | atexit(console_exit_handler); 63 | } 64 | //----------------------------------------------------------------- 65 | // putchar: 66 | //----------------------------------------------------------------- 67 | int console::putchar(int ch) 68 | { 69 | fprintf(stderr, "%c", ch); 70 | return 0; 71 | } 72 | //----------------------------------------------------------------- 73 | // getchar: 74 | //----------------------------------------------------------------- 75 | int console::getchar(void) 76 | { 77 | char ch; 78 | if (read(STDIN_FILENO,&ch,1) == 1) 79 | return ch; 80 | return -1; 81 | } 82 | -------------------------------------------------------------------------------- /core/console.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __CONSOLE_H__ 9 | #define __CONSOLE_H__ 10 | 11 | #include "console_io.h" 12 | 13 | //----------------------------------------------------------------- 14 | // console: Simple polled console 15 | //----------------------------------------------------------------- 16 | class console: public console_io 17 | { 18 | public: 19 | console(); 20 | 21 | int putchar(int ch); 22 | int getchar(void); 23 | }; 24 | 25 | #endif -------------------------------------------------------------------------------- /core/console_io.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __CONSOLE_IO_H__ 9 | #define __CONSOLE_IO_H__ 10 | 11 | //-------------------------------------------------------------------- 12 | // Abstract interface for conio 13 | //-------------------------------------------------------------------- 14 | class console_io 15 | { 16 | public: 17 | virtual int putchar(int ch) = 0; 18 | virtual int getchar(void) = 0; 19 | }; 20 | 21 | #endif -------------------------------------------------------------------------------- /core/cpu.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "cpu.h" 14 | 15 | //----------------------------------------------------------------- 16 | // Constructor 17 | //----------------------------------------------------------------- 18 | cpu::cpu() 19 | { 20 | m_memories = NULL; 21 | m_devices = NULL; 22 | m_console = NULL; 23 | m_has_breakpoints = false; 24 | m_stopped = false; 25 | m_fault = false; 26 | m_break = false; 27 | m_trace = 0; 28 | m_syscall_if = NULL; 29 | } 30 | //----------------------------------------------------------------- 31 | // error: Handle an error 32 | //----------------------------------------------------------------- 33 | bool cpu::error(bool is_fatal, const char *fmt, ...) 34 | { 35 | va_list args; 36 | 37 | va_start(args, fmt); 38 | vprintf(fmt, args); 39 | va_end(args); 40 | 41 | if (is_fatal) 42 | exit(-1); 43 | 44 | return true; 45 | } 46 | //----------------------------------------------------------------- 47 | // create_memory: Create a memory region 48 | //----------------------------------------------------------------- 49 | bool cpu::create_memory(uint32_t baseAddr, uint32_t len, uint8_t *buf /*=NULL*/) 50 | { 51 | // Avoid adding duplicate memories 52 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 53 | if (mem->valid_addr(baseAddr) && mem->valid_addr(baseAddr + len -1)) 54 | return true; 55 | 56 | return attach_memory(new memory("mem", baseAddr, len, buf)); 57 | } 58 | //----------------------------------------------------------------- 59 | // attach_memory: Attach a memory device to a particular region 60 | //----------------------------------------------------------------- 61 | bool cpu::attach_memory(memory_base *memory) 62 | { 63 | assert(memory->next == NULL); 64 | 65 | memory->next = m_memories; 66 | m_memories = memory; 67 | memory->reset(); 68 | 69 | return true; 70 | } 71 | //----------------------------------------------------------------- 72 | // attach_memory: Attach a memory device to a particular region 73 | //----------------------------------------------------------------- 74 | bool cpu::attach_device(device *dev) 75 | { 76 | // Devices are memory mapped 77 | attach_memory(dev); 78 | 79 | assert(dev->device_next == NULL); 80 | 81 | dev->device_next = m_devices; 82 | m_devices = dev; 83 | dev->reset(); 84 | 85 | return true; 86 | } 87 | //----------------------------------------------------------------- 88 | // get_break: Get breakpoint status (and clear) 89 | //----------------------------------------------------------------- 90 | bool cpu::get_break(void) 91 | { 92 | bool brk = m_break; 93 | m_break = false; 94 | return brk; 95 | } 96 | //----------------------------------------------------------------- 97 | // set_breakpoint: Set breakpoint on a given PC 98 | //----------------------------------------------------------------- 99 | bool cpu::set_breakpoint(uint32_t pc) 100 | { 101 | m_breakpoints.push_back(pc); 102 | m_has_breakpoints = true; 103 | return true; 104 | } 105 | //----------------------------------------------------------------- 106 | // set_breakpoint: Clear breakpoint on a given PC 107 | //----------------------------------------------------------------- 108 | bool cpu::clr_breakpoint(uint32_t pc) 109 | { 110 | for (std::vector::iterator it = m_breakpoints.begin() ; it != m_breakpoints.end(); ++it) 111 | if ((*it) == pc) 112 | { 113 | m_breakpoints.erase(it); 114 | m_has_breakpoints = !m_breakpoints.empty(); 115 | return true; 116 | } 117 | 118 | return false; 119 | } 120 | //----------------------------------------------------------------- 121 | // check_breakpoint: Check if breakpoint has been hit 122 | //----------------------------------------------------------------- 123 | bool cpu::check_breakpoint(uint32_t pc) 124 | { 125 | for (std::vector::iterator it = m_breakpoints.begin() ; it != m_breakpoints.end(); ++it) 126 | if ((*it) == pc) 127 | return true; 128 | 129 | return false; 130 | } 131 | //----------------------------------------------------------------- 132 | // valid_addr: Check if the physical memory address is valid 133 | //----------------------------------------------------------------- 134 | bool cpu::valid_addr(uint32_t address) 135 | { 136 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 137 | if (mem->valid_addr(address)) 138 | return true; 139 | 140 | return false; 141 | } 142 | //----------------------------------------------------------------- 143 | // write: Write a byte to memory (physical address) 144 | //----------------------------------------------------------------- 145 | void cpu::write(uint32_t address, uint8_t data) 146 | { 147 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 148 | if (mem->valid_addr(address)) 149 | { 150 | mem->write8(address, data); 151 | return ; 152 | } 153 | 154 | error(false, "Failed store @ 0x%08x\n", address); 155 | } 156 | //----------------------------------------------------------------- 157 | // read: Read a byte from memory (physical address) 158 | //----------------------------------------------------------------- 159 | uint8_t cpu::read(uint32_t address) 160 | { 161 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 162 | if (mem->valid_addr(address)) 163 | { 164 | uint8_t data = 0; 165 | mem->read8(address, data); 166 | return data; 167 | } 168 | 169 | return 0; 170 | } 171 | //----------------------------------------------------------------- 172 | // write16: Write a word to memory (physical address) 173 | //----------------------------------------------------------------- 174 | void cpu::write16(uint32_t address, uint16_t data) 175 | { 176 | address &= ~1; 177 | 178 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 179 | if (mem->valid_addr(address)) 180 | { 181 | mem->write16(address, data); 182 | return ; 183 | } 184 | 185 | error(false, "Failed store @ 0x%08x\n", address); 186 | } 187 | //----------------------------------------------------------------- 188 | // read16: Read a word from memory (physical address) 189 | //----------------------------------------------------------------- 190 | uint16_t cpu::read16(uint32_t address) 191 | { 192 | address &= ~1; 193 | 194 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 195 | if (mem->valid_addr(address)) 196 | { 197 | uint16_t data = 0; 198 | mem->read16(address, data); 199 | return data; 200 | } 201 | 202 | return 0; 203 | } 204 | //----------------------------------------------------------------- 205 | // write32: Write a word to memory (physical address) 206 | //----------------------------------------------------------------- 207 | void cpu::write32(uint32_t address, uint32_t data) 208 | { 209 | address &= ~3; 210 | 211 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 212 | if (mem->valid_addr(address)) 213 | { 214 | mem->write32(address, data); 215 | return ; 216 | } 217 | 218 | error(false, "Failed store @ 0x%08x\n", address); 219 | } 220 | //----------------------------------------------------------------- 221 | // read32: Read a word from memory (physical address) 222 | //----------------------------------------------------------------- 223 | uint32_t cpu::read32(uint32_t address) 224 | { 225 | address &= ~3; 226 | 227 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 228 | if (mem->valid_addr(address)) 229 | { 230 | uint32_t data = 0; 231 | mem->read32(address, data); 232 | return data; 233 | } 234 | 235 | return 0; 236 | } 237 | //----------------------------------------------------------------- 238 | // ifetch32: Read a instruction from memory (physical address) 239 | //----------------------------------------------------------------- 240 | uint32_t cpu::ifetch32(uint32_t address) 241 | { 242 | address &= ~3; 243 | 244 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 245 | if (mem->valid_addr(address)) 246 | { 247 | uint32_t data = 0; 248 | mem->ifetch32(address, data); 249 | return data; 250 | } 251 | 252 | return 0; 253 | } 254 | //----------------------------------------------------------------- 255 | // ifetch16: Read a instruction from memory (physical address) 256 | //----------------------------------------------------------------- 257 | uint16_t cpu::ifetch16(uint32_t address) 258 | { 259 | address &= ~1; 260 | 261 | for (memory_base *mem = m_memories; mem != NULL; mem = mem->next) 262 | if (mem->valid_addr(address)) 263 | { 264 | uint16_t data = 0; 265 | mem->ifetch16(address, data); 266 | return data; 267 | } 268 | 269 | return 0; 270 | } 271 | //----------------------------------------------------------------- 272 | // step: Step through one instruction 273 | //----------------------------------------------------------------- 274 | void cpu::step(void) 275 | { 276 | // Breakpoint hit? 277 | if (m_has_breakpoints && check_breakpoint(get_pc())) 278 | m_break = true; 279 | 280 | // Clock peripherals 281 | for (device *dev = m_devices; dev != NULL; dev = dev->device_next) 282 | { 283 | dev->clock(); 284 | 285 | if (dev->event_irq_raised()) 286 | set_interrupt(dev->get_irq_num()); 287 | else if (dev->event_irq_dropped()) 288 | clr_interrupt(dev->get_irq_num()); 289 | } 290 | } 291 | //----------------------------------------------------------------- 292 | // find_device: Find device by name and index 293 | //----------------------------------------------------------------- 294 | device * cpu::find_device(std::string name, int idx) 295 | { 296 | int count = 0; 297 | for (device *d = m_devices; d; d = d->device_next) 298 | { 299 | if (d->get_name() == name) 300 | { 301 | if (idx == count) 302 | return d; 303 | count++; 304 | } 305 | } 306 | 307 | return NULL; 308 | } 309 | -------------------------------------------------------------------------------- /core/cpu.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __CPU_H__ 9 | #define __CPU_H__ 10 | 11 | #include 12 | #include 13 | #include "memory.h" 14 | #include "device.h" 15 | #include "mem_api.h" 16 | #include "console_io.h" 17 | #include "syscall_if.h" 18 | 19 | //-------------------------------------------------------------------- 20 | // CPU model base class 21 | //-------------------------------------------------------------------- 22 | class cpu: public mem_api 23 | { 24 | public: 25 | cpu(); 26 | 27 | // mem_api 28 | virtual bool create_memory(uint32_t addr, uint32_t size, uint8_t *mem = NULL); 29 | virtual bool valid_addr(uint32_t addr); 30 | virtual void write(uint32_t addr, uint8_t data); 31 | virtual uint8_t read(uint32_t addr); 32 | 33 | // Memory access helpers 34 | virtual bool attach_memory(memory_base *memory); 35 | virtual void write16(uint32_t address, uint16_t data); 36 | virtual uint16_t read16(uint32_t address); 37 | virtual void write32(uint32_t address, uint32_t data); 38 | virtual uint32_t read32(uint32_t address); 39 | virtual uint32_t ifetch32(uint32_t address); 40 | virtual uint16_t ifetch16(uint32_t address); 41 | 42 | // Attach peripherals 43 | virtual bool attach_device(device * device); 44 | 45 | // Reset core to execute from specified PC 46 | virtual void reset(uint32_t pc) = 0; 47 | 48 | // Status 49 | virtual bool get_fault(void) { return m_fault; } 50 | virtual bool get_stopped(void) { return m_stopped; } 51 | 52 | // Execute one instruction 53 | virtual void step(void); 54 | 55 | // Breakpoints 56 | virtual bool get_break(void); 57 | virtual bool set_breakpoint(uint32_t pc); 58 | virtual bool clr_breakpoint(uint32_t pc); 59 | virtual bool check_breakpoint(uint32_t pc); 60 | 61 | // Syscall hosting (semi-hosting) 62 | virtual bool syscall_handler(void) 63 | { return m_syscall_if ? m_syscall_if->syscall_handler(this) : false; } 64 | virtual void set_syscall_handler(syscall_if *sys_if) 65 | { m_syscall_if = sys_if; } 66 | 67 | // State after execution 68 | virtual uint32_t get_opcode(void) = 0; 69 | virtual uint32_t get_pc(void) = 0; 70 | virtual uint64_t get_pc64(void) { return get_pc(); } 71 | virtual uint32_t get_register(int r) = 0; 72 | virtual uint64_t get_register64(int r) { return 0; } 73 | virtual int get_reg_width(void) { return 32; } // Default 74 | virtual int get_num_reg(void) = 0; 75 | 76 | virtual void set_register(int r, uint32_t val) = 0; 77 | virtual void set_pc(uint32_t val) = 0; 78 | 79 | virtual void set_register(int r, uint64_t val) { } 80 | virtual void set_pc(uint64_t val) { } 81 | 82 | // First register for args in ABI 83 | virtual int get_abi_reg_arg0(void) = 0; 84 | 85 | // Number of registers (max) used for args 86 | virtual int get_abi_reg_num(void) = 0; 87 | 88 | // Get return register 89 | virtual int get_abi_reg_ret(void) { return get_abi_reg_arg0(); } 90 | 91 | // Trigger interrupt 92 | virtual void set_interrupt(int irq) = 0; 93 | virtual void clr_interrupt(int irq) = 0; 94 | 95 | // Instruction trace 96 | virtual void enable_trace(uint32_t mask) { m_trace = mask; } 97 | 98 | // Monitor executed instructions 99 | virtual void log_exception(uint64_t src, uint64_t dst, uint64_t cause) { } 100 | virtual void log_branch(uint64_t src, uint64_t dst, bool taken) { } 101 | virtual void log_branch_jump(uint64_t src, uint64_t dst) { } 102 | virtual void log_branch_call(uint64_t src, uint64_t dst) { } 103 | virtual void log_branch_ret(uint64_t src, uint64_t dst) { } 104 | virtual void log_commit_pc(uint64_t pc) { } 105 | 106 | // Stats 107 | virtual void stats_reset(void) = 0; 108 | virtual void stats_dump(void) = 0; 109 | 110 | // Console 111 | void set_console(console_io *cio) { m_console = cio; } 112 | 113 | // Error message 114 | bool error(bool is_fatal, const char *fmt, ...); 115 | 116 | // Find device by name and index 117 | device * find_device(std::string name, int idx); 118 | 119 | protected: 120 | // Memory 121 | memory_base *m_memories; 122 | device *m_devices; 123 | 124 | // Status 125 | bool m_stopped; 126 | bool m_fault; 127 | bool m_break; 128 | int m_trace; 129 | 130 | // Breakpoints 131 | bool m_has_breakpoints; 132 | std::vector m_breakpoints; 133 | 134 | // Console 135 | console_io *m_console; 136 | 137 | // System call hosting 138 | syscall_if *m_syscall_if; 139 | }; 140 | 141 | #endif 142 | -------------------------------------------------------------------------------- /core/device.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_H__ 9 | #define __DEVICE_H__ 10 | 11 | #include "memory.h" 12 | 13 | //-------------------------------------------------------------------- 14 | // Device base class 15 | //-------------------------------------------------------------------- 16 | class device: public memory_base 17 | { 18 | public: 19 | device(std::string name, uint32_t base, uint32_t size, device *irq_ctrl = NULL, int irq = -1): 20 | memory_base(name, base, size) 21 | { 22 | m_irq_ctrl = irq_ctrl; 23 | m_irq_number = irq; 24 | m_irq_raised = false; 25 | m_irq_dropped= false; 26 | device_next = NULL; 27 | } 28 | 29 | virtual void set_irq(int irq) { } 30 | virtual void clr_irq(int irq) { } 31 | 32 | virtual int get_irq_num(void) 33 | { 34 | return m_irq_number; 35 | } 36 | 37 | virtual void raise_interrupt(void) 38 | { 39 | if (m_irq_ctrl) 40 | m_irq_ctrl->set_irq(m_irq_number); 41 | else 42 | m_irq_raised = true; 43 | } 44 | 45 | virtual void drop_interrupt(void) 46 | { 47 | if (m_irq_ctrl) 48 | m_irq_ctrl->clr_irq(m_irq_number); 49 | else 50 | m_irq_dropped = true; 51 | } 52 | 53 | virtual bool event_irq_raised(void) 54 | { 55 | bool val = m_irq_raised; 56 | m_irq_raised = false; 57 | return val; 58 | } 59 | 60 | virtual bool event_irq_dropped(void) 61 | { 62 | bool val = m_irq_dropped; 63 | m_irq_dropped = false; 64 | return val; 65 | } 66 | 67 | virtual int min_access_size(void) { return 4; } 68 | 69 | virtual bool write8(uint32_t addr, uint8_t data) 70 | { 71 | printf("ERROR: write8 not supported @ 0x%08x\n", addr); 72 | return false; 73 | } 74 | 75 | virtual bool write_block(uint32_t addr, uint8_t *data, int length) 76 | { 77 | printf("ERROR: write_block not supported @ 0x%08x\n", addr); 78 | return false; 79 | } 80 | 81 | virtual bool read8(uint32_t addr, uint8_t &data) 82 | { 83 | printf("ERROR: read8 not supported @ 0x%08x\n", addr); 84 | return false; 85 | } 86 | 87 | virtual bool read_block(uint32_t addr, uint8_t *data, int length) 88 | { 89 | printf("ERROR: read_block not supported @ 0x%08x\n", addr); 90 | return false; 91 | } 92 | 93 | public: 94 | device* device_next; 95 | 96 | protected: 97 | int m_irq_number; 98 | device * m_irq_ctrl; 99 | bool m_irq_raised; 100 | bool m_irq_dropped; 101 | }; 102 | 103 | #endif 104 | -------------------------------------------------------------------------------- /core/elf_load.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "elf_load.h" 19 | 20 | //-------------------------------------------------------------------- 21 | // Constructor 22 | //-------------------------------------------------------------------- 23 | elf_load::elf_load(const char *filename, mem_api *target, bool load_to_paddr /*= false*/, int64_t load_offset /*= 0*/) 24 | { 25 | m_filename = std::string(filename); 26 | m_target = target; 27 | m_entry_point = 0; 28 | m_load_to_paddr = load_to_paddr; 29 | m_load_offset = load_offset; 30 | } 31 | //-------------------------------------------------------------------- 32 | // load: Load ELF to target 33 | //-------------------------------------------------------------------- 34 | bool elf_load::load(void) 35 | { 36 | int fd; 37 | Elf * e; 38 | Elf_Kind ek; 39 | Elf_Scn *scn; 40 | Elf_Data *data; 41 | size_t shstrndx; 42 | int num_phdrs = 0; 43 | 44 | if (elf_version ( EV_CURRENT ) == EV_NONE) 45 | return false; 46 | 47 | if ((fd = open ( m_filename.c_str() , O_RDONLY , 0)) < 0) 48 | return false; 49 | 50 | if ((e = elf_begin ( fd , ELF_C_READ, NULL )) == NULL) 51 | return false; 52 | 53 | ek = elf_kind ( e ); 54 | if (ek != ELF_K_ELF) 55 | return false; 56 | 57 | // Get section name header index 58 | if (elf_getshdrstrndx(e, &shstrndx)!=0) 59 | return false; 60 | 61 | // Get entry point 62 | { 63 | GElf_Ehdr _ehdr; 64 | GElf_Ehdr *ehdr = gelf_getehdr(e, &_ehdr); 65 | m_entry_point = ehdr ? (uint32_t)ehdr->e_entry : 0; 66 | num_phdrs = ehdr ? ehdr->e_phnum : 0; 67 | } 68 | 69 | int section_idx = 0; 70 | while ((scn = elf_getscn(e, section_idx)) != NULL) 71 | { 72 | Elf32_Shdr *shdr = elf32_getshdr(scn); 73 | 74 | // 64-bit target 75 | if (!shdr) 76 | { 77 | Elf64_Shdr *shdr64 = elf64_getshdr(scn); 78 | 79 | if ((shdr64->sh_flags & SHF_ALLOC) && (shdr64->sh_size > 0)) 80 | { 81 | data = elf_getdata(scn, NULL); 82 | uint64_t base_addr = shdr64->sh_addr + m_load_offset; 83 | 84 | printf("Memory: 0x%lx - 0x%lx (Size=%ldKB) [%s]\n", shdr64->sh_addr, shdr64->sh_addr + shdr64->sh_size - 1, shdr64->sh_size / 1024, elf_strptr(e, shstrndx, shdr64->sh_name)); 85 | 86 | // Load to physical address instead of the virtual target? 87 | if (m_load_to_paddr) 88 | { 89 | Elf64_Phdr *ptbl = elf64_getphdr(e); 90 | for (int i=0;ip_vaddr) 92 | { 93 | base_addr = ptbl->p_paddr; 94 | break; 95 | } 96 | } 97 | 98 | if (!m_target->create_memory(base_addr, shdr64->sh_size)) 99 | { 100 | fprintf(stderr, "ERROR: Cannot allocate memory region\n"); 101 | close (fd); 102 | return false; 103 | } 104 | 105 | if (shdr64->sh_type == SHT_PROGBITS) 106 | { 107 | int i; 108 | for (i=0;ish_size;i++) 109 | { 110 | uint32_t load_addr = base_addr + i; 111 | if (m_target->valid_addr(load_addr)) 112 | m_target->write(load_addr, ((uint8_t*)data->d_buf)[i]); 113 | else 114 | { 115 | fprintf(stderr, "ERROR: Cannot write byte to 0x%08x\n", load_addr); 116 | close (fd); 117 | return false; 118 | } 119 | } 120 | } 121 | } 122 | } 123 | // 32-bit target - section which need allocating 124 | else if ((shdr->sh_flags & SHF_ALLOC) && (shdr->sh_size > 0)) 125 | { 126 | data = elf_getdata(scn, NULL); 127 | uint32_t base_addr = shdr->sh_addr + m_load_offset; 128 | 129 | printf("Memory: 0x%x - 0x%x (Size=%dKB) [%s]\n", shdr->sh_addr, shdr->sh_addr + shdr->sh_size - 1, shdr->sh_size / 1024, elf_strptr(e, shstrndx, shdr->sh_name)); 130 | 131 | // Load to physical address instead of the virtual target? 132 | if (m_load_to_paddr) 133 | { 134 | Elf32_Phdr *ptbl = elf32_getphdr(e); 135 | for (int i=0;ip_vaddr) 137 | { 138 | base_addr = ptbl->p_paddr; 139 | break; 140 | } 141 | } 142 | 143 | if (!m_target->create_memory(base_addr, shdr->sh_size)) 144 | { 145 | fprintf(stderr, "ERROR: Cannot allocate memory region\n"); 146 | close (fd); 147 | return false; 148 | } 149 | 150 | if (shdr->sh_type == SHT_PROGBITS) 151 | { 152 | int i; 153 | for (i=0;ish_size;i++) 154 | { 155 | uint32_t load_addr = base_addr + i; 156 | if (m_target->valid_addr(load_addr)) 157 | m_target->write(load_addr, ((uint8_t*)data->d_buf)[i]); 158 | else 159 | { 160 | fprintf(stderr, "ERROR: Cannot write byte to 0x%08x\n", load_addr); 161 | close (fd); 162 | return false; 163 | } 164 | } 165 | } 166 | } 167 | 168 | section_idx++; 169 | } 170 | 171 | elf_end ( e ); 172 | close ( fd ); 173 | 174 | return true; 175 | } 176 | //-------------------------------------------------------------------- 177 | // get_symbol: Get symbol from ELF 178 | //-------------------------------------------------------------------- 179 | bool elf_load::get_symbol(const char *symname, uint32_t &value) 180 | { 181 | bfd *ibfd; 182 | asymbol **symtab; 183 | long nsize, nsyms, i; 184 | symbol_info syminfo; 185 | char **matching; 186 | 187 | bfd_init(); 188 | 189 | ibfd = bfd_openr(m_filename.c_str(), NULL); 190 | if (ibfd == NULL) 191 | { 192 | printf("ERROR: get_symbol: bfd_openr error\n"); 193 | return false; 194 | } 195 | 196 | if (!bfd_check_format_matches(ibfd, bfd_object, &matching)) 197 | { 198 | printf("ERROR: get_symbol: format_matches\n"); 199 | return false; 200 | } 201 | 202 | nsize = bfd_get_symtab_upper_bound (ibfd); 203 | symtab = (asymbol **)malloc(nsize); 204 | nsyms = bfd_canonicalize_symtab(ibfd, symtab); 205 | 206 | bool found = false; 207 | 208 | for (i = 0; i < nsyms; i++) 209 | { 210 | if (strcmp(symtab[i]->name, symname) == 0) 211 | { 212 | bfd_symbol_info(symtab[i], &syminfo); 213 | value = syminfo.value; 214 | found = true; 215 | break; 216 | } 217 | } 218 | 219 | bfd_close(ibfd); 220 | 221 | return found; 222 | } 223 | -------------------------------------------------------------------------------- /core/elf_load.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __ELF_LOAD_H__ 9 | #define __ELF_LOAD_H__ 10 | 11 | #include "mem_api.h" 12 | #include 13 | 14 | //-------------------------------------------------------------------- 15 | // ELF loader 16 | //-------------------------------------------------------------------- 17 | class elf_load 18 | { 19 | public: 20 | elf_load(const char *filename, mem_api *target, bool load_to_paddr = false, int64_t load_offset = 0); 21 | 22 | bool load(void); 23 | uint32_t get_entry_point(void) { return m_entry_point; } 24 | bool get_symbol(const char *symname, uint32_t &value); 25 | 26 | protected: 27 | std::string m_filename; 28 | mem_api * m_target; 29 | uint32_t m_entry_point; 30 | bool m_load_to_paddr; 31 | int64_t m_load_offset; 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /core/mem_api.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __MEM_API_H__ 9 | #define __MEM_API_H__ 10 | 11 | #include 12 | 13 | //-------------------------------------------------------------------- 14 | // Abstract interface for memory access 15 | //-------------------------------------------------------------------- 16 | class mem_api 17 | { 18 | public: 19 | virtual bool create_memory(uint32_t addr, uint32_t size, uint8_t *mem = NULL) = 0; 20 | virtual bool valid_addr(uint32_t addr) = 0; 21 | virtual void write(uint32_t addr, uint8_t data) = 0; 22 | virtual uint8_t read(uint32_t addr) = 0; 23 | }; 24 | 25 | #endif -------------------------------------------------------------------------------- /core/memory.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __MEMORY_H__ 9 | #define __MEMORY_H__ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | //-------------------------------------------------------------------- 16 | // Base interface for memories / devices 17 | //-------------------------------------------------------------------- 18 | class memory_base 19 | { 20 | public: 21 | memory_base(std::string name, uint32_t base, uint32_t size) 22 | { 23 | m_base = base; 24 | m_size = size; 25 | m_name = name; 26 | m_trace = false; 27 | next = NULL; 28 | } 29 | 30 | std::string get_name(void) { return m_name; } 31 | void enable_trace(bool en) { m_trace = en; } 32 | 33 | // Reset / Init 34 | virtual void reset(void) { } 35 | 36 | // Address range check 37 | virtual bool valid_addr(uint32_t addr) { return (addr >= m_base) && (addr < (m_base + m_size)); } 38 | 39 | // Write Access 40 | virtual bool write8(uint32_t addr, uint8_t data) = 0; 41 | virtual bool write16(uint32_t addr, uint32_t data) 42 | { 43 | bool res = true; 44 | if (m_trace) 45 | printf("%s: write16 0x%08x=0x%08x\n", m_name.c_str(), addr, data); 46 | for (int i=0;i<2;i++) 47 | res &= write8(addr + i, data >> (8*i)); 48 | return res; 49 | } 50 | virtual bool write32(uint32_t addr, uint32_t data) 51 | { 52 | bool res = true; 53 | if (m_trace) 54 | printf("%s: write32 0x%08x=0x%08x\n", m_name.c_str(), addr, data); 55 | for (int i=0;i<4;i++) 56 | res &= write8(addr + i, data >> (8*i)); 57 | return res; 58 | } 59 | virtual bool write_block(uint32_t addr, uint8_t *data, int length) 60 | { 61 | bool res = true; 62 | if (m_trace) 63 | printf("%s: write 0x%08x length %d\n", m_name.c_str(), addr, length); 64 | for (int i=0;i 12 | #include 13 | #include "memory.h" 14 | #include "cpu.h" 15 | #include "device_systick.h" 16 | 17 | //-------------------------------------------------------------------- 18 | // armv6m: Simple ARM v6m model 19 | //-------------------------------------------------------------------- 20 | class armv6m: public cpu 21 | { 22 | public: 23 | armv6m(uint32_t baseAddr = 0, uint32_t len = 0); 24 | 25 | void reset(uint32_t start_addr); 26 | uint32_t get_opcode(uint32_t pc); 27 | void step(void); 28 | 29 | void set_interrupt(int irq); 30 | void clr_interrupt(int irq) { } 31 | 32 | bool get_reg_valid(int r) { return true; } 33 | uint32_t get_register(int r); 34 | 35 | uint32_t get_pc(void); 36 | uint32_t get_opcode(void) { return get_opcode(get_pc()); } 37 | int get_num_reg(void) { return 16; } 38 | 39 | void set_register(int r, uint32_t val); 40 | void set_pc(uint32_t val); 41 | 42 | void stats_reset(void) { } 43 | void stats_dump(void) { } 44 | 45 | // First register for args in ABI 46 | int get_abi_reg_arg0(void) { return 0; } 47 | 48 | // Number of registers (max) used for args 49 | int get_abi_reg_num(void) { return 4; } 50 | 51 | void set_flag(uint32_t flag, bool val); 52 | bool get_flag(uint32_t flag); 53 | 54 | protected: 55 | uint16_t armv6m_read_inst(uint32_t addr); 56 | void armv6m_update_sp(uint32_t sp); 57 | void armv6m_update_n_z_flags(uint32_t rd); 58 | uint32_t armv6m_add_with_carry(uint32_t rn, uint32_t rm, uint32_t carry_in, uint32_t mask); 59 | uint32_t armv6m_shift_left(uint32_t val, uint32_t shift, uint32_t mask); 60 | uint32_t armv6m_shift_right(uint32_t val, uint32_t shift, uint32_t mask); 61 | uint32_t armv6m_arith_shift_right(uint32_t val, uint32_t shift, uint32_t mask); 62 | uint32_t armv6m_rotate_right(uint32_t val, uint32_t shift, uint32_t mask); 63 | uint32_t armv6m_sign_extend(uint32_t val, int offset); 64 | void armv6m_dump_inst(uint16_t inst); 65 | uint32_t armv6m_exception(uint32_t pc, uint32_t exception); 66 | void armv6m_exc_return(uint32_t pc); 67 | 68 | public: 69 | int armv6m_decode(uint16_t inst); 70 | void armv6m_execute(uint16_t inst, uint16_t inst2); 71 | 72 | protected: 73 | 74 | // Registers 75 | uint32_t m_regfile[16]; 76 | uint32_t m_psp; // Process stack pointer 77 | uint32_t m_msp; // Main stack pointer 78 | uint32_t m_apsr; 79 | uint32_t m_ipsr; 80 | uint32_t m_epsr; 81 | 82 | uint32_t m_primask; 83 | uint32_t m_control; 84 | 85 | typedef enum { MODE_THREAD = 0, MODE_HANDLER } tMode; 86 | tMode m_current_mode; 87 | 88 | uint32_t m_entry_point; 89 | 90 | // Decode 91 | int m_inst_group; 92 | uint32_t m_rd; 93 | uint32_t m_rt; 94 | uint32_t m_rm; 95 | uint32_t m_rn; 96 | uint32_t m_imm; 97 | uint32_t m_cond; 98 | uint32_t m_reglist; 99 | 100 | // Built in peripherals 101 | device_systick * m_systick; 102 | bool m_systick_irq; 103 | }; 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /cpu-mips-i/mips.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __MIPS_H__ 9 | #define __MIPS_H__ 10 | 11 | #include 12 | #include 13 | #include "memory.h" 14 | #include "cpu.h" 15 | 16 | //-------------------------------------------------------------------- 17 | // Class 18 | //-------------------------------------------------------------------- 19 | class mips_i: public cpu 20 | { 21 | public: 22 | mips_i(uint32_t baseAddr = 0, uint32_t len = 0); 23 | 24 | void reset(uint32_t start_addr); 25 | uint32_t get_opcode(uint32_t pc); 26 | void step(void); 27 | 28 | void set_interrupt(int irq); 29 | void clr_interrupt(int irq) { } 30 | 31 | uint32_t get_register(int reg); 32 | uint32_t get_pc() { return m_pc_x; } 33 | uint32_t get_next_pc(void) { return m_pc_next; } 34 | bool get_branch(void) { return m_branch_ds; } 35 | bool get_take_excpn(void) { return m_take_excpn; } 36 | uint32_t get_opcode(void) { return get_opcode(m_pc_x); } 37 | int get_num_reg(void) { return 32; } 38 | 39 | // First register for args in ABI 40 | int get_abi_reg_arg0(void) { return 4; } 41 | 42 | // Get return register 43 | int get_abi_reg_ret(void) { return 2; } 44 | 45 | // Number of registers (max) used for args 46 | int get_abi_reg_num(void) { return 4; } 47 | 48 | void set_register(int reg, uint32_t val); 49 | void set_pc(uint32_t val) { m_pc = val; m_pc_x = m_pc; } 50 | 51 | void stats_reset(void); 52 | void stats_dump(void); 53 | 54 | void enable_mem_errors(bool en) { m_enable_mem_errors = en; } 55 | 56 | protected: 57 | bool execute(void); 58 | int load(uint32_t pc, uint32_t address, uint32_t *result, int width, bool signedLoad); 59 | int store(uint32_t pc, uint32_t address, uint32_t data, int width, uint8_t mask); 60 | void exception(uint32_t cause, uint32_t pc, uint32_t badaddr = 0); 61 | 62 | virtual int copro0_inst(uint32_t pc, uint32_t opc, uint32_t reg_rs, uint32_t reg_rt, int &wb_reg, uint32_t &result); 63 | virtual int copro_inst(int cop, uint32_t pc, uint32_t opc, uint32_t reg_rs, uint32_t reg_rt, int &wb_reg, uint32_t &result); 64 | 65 | private: 66 | 67 | // Other registers 68 | uint32_t m_pc; 69 | uint32_t m_pc_next; 70 | uint32_t m_pc_x; 71 | uint32_t m_epc; 72 | uint32_t m_status; 73 | uint32_t m_cause; 74 | uint32_t m_badaddr; 75 | uint32_t m_hi; 76 | uint32_t m_lo; 77 | uint32_t m_isr_vector; 78 | bool m_branch_ds; 79 | bool m_take_excpn; 80 | bool m_enable_mem_errors; 81 | uint32_t m_cycles; 82 | uint32_t m_gpr[32]; 83 | 84 | enum eStatsMips 85 | { 86 | STATS_MIN, 87 | STATS_INSTRUCTIONS = STATS_MIN, 88 | STATS_LOADS, 89 | STATS_STORES, 90 | STATS_BRANCHES, 91 | STATS_COPRO, 92 | STATS_NOP, 93 | STATS_EXCEPTIONS, 94 | STATS_MAX 95 | }; 96 | 97 | // Stats 98 | uint32_t m_stats[STATS_MAX]; 99 | }; 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /cpu-mips-i/mips_isa.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __MIPS_ISA_H__ 9 | #define __MIPS_ISA_H__ 10 | 11 | //----------------------------------------------------------------- 12 | // General: 13 | //----------------------------------------------------------------- 14 | enum ERegisters 15 | { 16 | REG_0_ZERO, 17 | REG_1, 18 | REG_2, 19 | REG_3, 20 | REG_4, 21 | REG_5, 22 | REG_6, 23 | REG_7, 24 | REG_8, 25 | REG_9, 26 | REG_10, 27 | REG_11, 28 | REG_12, 29 | REG_13, 30 | REG_14, 31 | REG_15, 32 | REG_16, 33 | REG_17, 34 | REG_18, 35 | REG_19, 36 | REG_20, 37 | REG_21, 38 | REG_22, 39 | REG_23, 40 | REG_24, 41 | REG_25, 42 | REG_26, 43 | REG_27, 44 | REG_28_GP, 45 | REG_29_SP, 46 | REG_30_FP, 47 | REG_31_RA, 48 | REGISTERS 49 | }; 50 | 51 | //-------------------------------------------------------------------- 52 | // Instruction Encoding 53 | //-------------------------------------------------------------------- 54 | #define OPCODE_INST_MASK 0x3F 55 | #define OPCODE_INST_SHIFT 26 56 | 57 | #define OPCODE_RS_MASK 0x1f 58 | #define OPCODE_RS_SHIFT 21 59 | 60 | #define OPCODE_RT_MASK 0x1f 61 | #define OPCODE_RT_SHIFT 16 62 | 63 | #define OPCODE_RD_MASK 0x1f 64 | #define OPCODE_RD_SHIFT 11 65 | 66 | #define OPCODE_RE_MASK 0x1f 67 | #define OPCODE_RE_SHIFT 6 68 | 69 | #define OPCODE_FUNC_MASK 0x3f 70 | #define OPCODE_FUNC_SHIFT 0 71 | // or 72 | #define OPCODE_IMM_MASK 0xFFFF 73 | #define OPCODE_IMM_SHIFT 0 74 | // or 75 | #define OPCODE_ADDR_SHIFT 0 76 | #define OPCODE_ADDR_MASK 0x3FFFFFF 77 | 78 | //-------------------------------------------------------------------- 79 | // R Type 80 | //-------------------------------------------------------------------- 81 | #define INSTR_R_SLL 0x00 82 | #define INSTR_R_SRL 0x02 83 | #define INSTR_R_SRA 0x03 84 | #define INSTR_R_SLLV 0x04 85 | #define INSTR_R_SRLV 0x06 86 | #define INSTR_R_SRAV 0x07 87 | #define INSTR_R_JR 0x08 88 | #define INSTR_R_JALR 0x09 89 | #define INSTR_R_SYSCALL 0x0c 90 | #define INSTR_R_BREAK 0x0d 91 | #define INSTR_R_MFHI 0x10 92 | #define INSTR_R_MTHI 0x11 93 | #define INSTR_R_MFLO 0x12 94 | #define INSTR_R_MTLO 0x13 95 | #define INSTR_R_MULT 0x18 96 | #define INSTR_R_MULTU 0x19 97 | #define INSTR_R_DIV 0x1a 98 | #define INSTR_R_DIVU 0x1b 99 | #define INSTR_R_ADD 0x20 100 | #define INSTR_R_ADDU 0x21 101 | #define INSTR_R_SUB 0x22 102 | #define INSTR_R_SUBU 0x23 103 | #define INSTR_R_AND 0x24 104 | #define INSTR_R_OR 0x25 105 | #define INSTR_R_XOR 0x26 106 | #define INSTR_R_NOR 0x27 107 | #define INSTR_R_SLT 0x2a 108 | #define INSTR_R_SLTU 0x2b 109 | 110 | #define INSTR_COP0 0x10 111 | #define INSTR_COP1 0x11 112 | #define INSTR_COP2 0x12 113 | #define INSTR_COP3 0x13 114 | 115 | //-------------------------------------------------------------------- 116 | // J Type 117 | //-------------------------------------------------------------------- 118 | #define INSTR_J_JAL 0x03 119 | #define INSTR_J_J 0x02 120 | #define INSTR_J_BEQ 0x04 121 | #define INSTR_J_BNE 0x05 122 | #define INSTR_J_BLEZ 0x06 123 | #define INSTR_J_BGTZ 0x07 124 | 125 | //-------------------------------------------------------------------- 126 | // I Type 127 | //-------------------------------------------------------------------- 128 | #define INSTR_I_ADDI 0x08 129 | #define INSTR_I_ADDIU 0x09 130 | #define INSTR_I_SLTI 0x0a 131 | #define INSTR_I_SLTIU 0x0b 132 | #define INSTR_I_ANDI 0x0c 133 | #define INSTR_I_ORI 0x0d 134 | #define INSTR_I_XORI 0x0e 135 | #define INSTR_I_LUI 0x0f 136 | #define INSTR_I_LB 0x20 137 | #define INSTR_I_LH 0x21 138 | #define INSTR_I_LW 0x23 139 | #define INSTR_I_LBU 0x24 140 | #define INSTR_I_LHU 0x25 141 | #define INSTR_I_SB 0x28 142 | #define INSTR_I_SH 0x29 143 | #define INSTR_I_SW 0x2b 144 | 145 | #define INSTR_I_REGIMM 0x01 146 | #define INSTR_I_COND_BLTZAL 0x10 147 | #define INSTR_I_COND_BLTZ 0x00 148 | #define INSTR_I_COND_BGEZAL 0x11 149 | #define INSTR_I_COND_BGEZ 0x01 150 | 151 | #define INSTR_I_LWL 0x22 152 | #define INSTR_I_LWR 0x26 153 | #define INSTR_I_SWL 0x2a 154 | #define INSTR_I_SWR 0x2e 155 | 156 | #define INSTR_I_LWC0 0x30 157 | #define INSTR_I_LWC1 0x31 158 | #define INSTR_I_LWC2 0x32 159 | #define INSTR_I_LWC3 0x33 160 | #define INSTR_I_SWC0 0x38 161 | #define INSTR_I_SWC1 0x39 162 | #define INSTR_I_SWC2 0x3a 163 | #define INSTR_I_SWC3 0x3b 164 | 165 | //-------------------------------------------------------------------- 166 | // COP0 167 | //-------------------------------------------------------------------- 168 | #define COP0_RFE 0x10 169 | #define COP0_MFC0 0x00 170 | #define COP0_MTC0 0x04 171 | 172 | #define COP0_STATUS 12 // Processor status and control 173 | #define COP0_SR_IEC 0 // Interrupt enable (current) 174 | #define COP0_SR_KUC 1 // User mode (current) 175 | #define COP0_SR_IEP 2 // Interrupt enable (previous) 176 | #define COP0_SR_KUP 3 // User mode (previous) 177 | #define COP0_SR_IEO 4 // Interrupt enable (old) 178 | #define COP0_SR_KUO 5 // User mode (old) 179 | #define COP0_SR_IM0 8 // Interrupt mask 180 | #define COP0_SR_IM_MASK 0xFF 181 | #define COP0_SR_CU0 28 // User mode enable to COPx 182 | #define COP0_SR_CU_MASK 0xF 183 | #define SR_BF_GET(v,f) (((v) >> (COP0_SR_##f)) & 0x1) 184 | #define SR_BF_GETM(v,f,m) (((v) >> (COP0_SR_##f)) & (m)) 185 | #define SR_BF_SET(v,f,n) (((v) & ~(1 << COP0_SR_##f)) | ((n) << COP0_SR_##f)) 186 | #define COP0_CAUSE 13 // Cause of last general exception 187 | #define COP0_CAUSE_EXC 2 188 | #define COP0_CAUSE_EXC_MASK 0x1F 189 | #define COP0_CAUSE_IP0 8 190 | #define COP0_CAUSE_IP_MASK 0xFF 191 | #define COP0_CAUSE_IV 23 192 | #define COP0_CAUSE_CE 28 193 | #define COP0_CAUSE_CE_MASK 0x7 194 | #define COP0_CAUSE_BD 31 195 | #define CAUSE_BF_GET(v,f,m) (((v) >> (COP0_CAUSE_##f)) & (m)) 196 | #define CAUSE_BF_SET(v,f,m,n) (((v) & ~((m) << COP0_CAUSE_##f)) | ((n) << COP0_CAUSE_##f)) 197 | #define COP0_EPC 14 // Program counter at last exception 198 | #define COP0_BADADDR 8 // Bad address value 199 | #define COP0_COUNT 9 // Processor cycle count 200 | #define COP0_PRID 15 // Processor identification and revision 201 | #define COP0_EBASE 15 // Exception vector base register 202 | 203 | //-------------------------------------------------------------------- 204 | // Exception Codes 205 | //-------------------------------------------------------------------- 206 | #define EXC_INT 0 // Interrupt 207 | #define EXC_ADEL 4 // Address error exception (load or instruction fetch) 208 | #define EXC_ADES 5 // Address error exception (store) 209 | #define EXC_IBE 6 // Bus error exception (instruction fetch) 210 | #define EXC_DBE 7 // Bus error exception (data reference: load or store) 211 | #define EXC_SYS 8 // Syscall exception 212 | #define EXC_BP 9 // Breakpoint exception 213 | #define EXC_RI 10 // Reserved instruction exception 214 | #define EXC_CPU 11 // Coprocessor Unusable exception 215 | #define EXC_OV 12 // Arithmetic Overflow exception 216 | 217 | #endif 218 | -------------------------------------------------------------------------------- /cpu-rv32/rv32.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __RV32_H__ 9 | #define __RV32_H__ 10 | 11 | #include 12 | #include 13 | #include "memory.h" 14 | #include "cpu.h" 15 | 16 | //-------------------------------------------------------------------- 17 | // rv32: RV32IM model 18 | //-------------------------------------------------------------------- 19 | class rv32: public cpu 20 | { 21 | public: 22 | rv32(uint32_t baseAddr = 0, uint32_t len = 0); 23 | 24 | void reset(uint32_t start_addr); 25 | uint32_t get_opcode(uint32_t pc); 26 | void step(void); 27 | 28 | void set_interrupt(int irq); 29 | void clr_interrupt(int irq); 30 | 31 | uint32_t get_register(int r); 32 | 33 | uint32_t get_pc(void) { return m_pc_x; } 34 | uint32_t get_opcode(void) { return get_opcode(m_pc_x); } 35 | int get_num_reg(void) { return 32; } 36 | 37 | void set_register(int r, uint32_t val); 38 | void set_pc(uint32_t val); 39 | 40 | void stats_reset(void); 41 | void stats_dump(void); 42 | 43 | void enable_mem_unaligned(bool en) { m_enable_unaligned = en; } 44 | void enable_mem_errors(bool en) { m_enable_mem_errors = en; } 45 | void enable_compliant_csr(bool en) { m_compliant_csr = en; } 46 | 47 | // First register for args in ABI 48 | int get_abi_reg_arg0(void) { return 10; } 49 | 50 | // Number of registers (max) used for args 51 | int get_abi_reg_num(void) { return 8; } 52 | 53 | // Enable / Disable ISA extensions 54 | void enable_rvm(bool en) { m_enable_rvm = en; } 55 | void enable_rvc(bool en) { m_enable_rvc = en; } 56 | void enable_rva(bool en) { m_enable_rva = en; } 57 | 58 | // SBI hosting support 59 | bool in_super_mode(void); 60 | void set_timer(uint32_t value); 61 | void sbi_boot(uint32_t boot_addr, uint32_t dtb_addr); 62 | 63 | protected: 64 | bool execute(void); 65 | int load(uint32_t pc, uint32_t address, uint32_t *result, int width, bool signedLoad); 66 | int store(uint32_t pc, uint32_t address, uint32_t data, int width); 67 | virtual bool access_csr(uint32_t address, uint32_t data, bool set, bool clr, uint32_t &result); 68 | void exception(uint32_t cause, uint32_t pc, uint32_t badaddr = 0); 69 | 70 | // MMU 71 | private: 72 | void mmu_flush(void); 73 | int mmu_read_word(uint32_t address, uint32_t *val); 74 | uint32_t mmu_walk(uint32_t addr); 75 | int mmu_i_translate(uint32_t addr, uint32_t *physical); 76 | int mmu_d_translate(uint32_t pc, uint32_t addr, uint32_t *physical, int writeNotRead); 77 | 78 | private: 79 | 80 | // CPU Registers 81 | uint32_t m_gpr[32]; 82 | uint32_t m_pc; 83 | uint32_t m_pc_x; 84 | uint32_t m_load_res; 85 | 86 | // CSR - Machine 87 | uint32_t m_csr_mepc; 88 | uint32_t m_csr_mcause; 89 | uint32_t m_csr_msr; 90 | uint32_t m_csr_mpriv; 91 | uint32_t m_csr_mevec; 92 | uint32_t m_csr_mtval; 93 | uint32_t m_csr_mie; 94 | uint32_t m_csr_mip; 95 | uint64_t m_csr_mtime; 96 | uint32_t m_csr_mtimecmp; 97 | bool m_csr_mtime_ie; 98 | uint32_t m_csr_mscratch; 99 | uint32_t m_csr_mideleg; 100 | uint32_t m_csr_medeleg; 101 | 102 | // CSR - Supervisor 103 | uint32_t m_csr_sepc; 104 | uint32_t m_csr_sevec; 105 | uint32_t m_csr_scause; 106 | uint32_t m_csr_stval; 107 | uint32_t m_csr_satp; 108 | uint32_t m_csr_sscratch; 109 | 110 | // TLB cache 111 | static const int MMU_TLB_ENTRIES = 64; 112 | uint32_t m_mmu_addr[MMU_TLB_ENTRIES]; 113 | uint32_t m_mmu_pte[MMU_TLB_ENTRIES]; 114 | 115 | // Settings 116 | bool m_enable_unaligned; 117 | bool m_enable_mem_errors; 118 | bool m_compliant_csr; 119 | bool m_enable_rvm; 120 | bool m_enable_rvc; 121 | bool m_enable_rva; 122 | bool m_enable_mtimecmp; 123 | bool m_enable_sbi; 124 | 125 | // Stats 126 | enum eStats 127 | { 128 | STATS_MIN, 129 | STATS_INSTRUCTIONS = STATS_MIN, 130 | STATS_LOADS, 131 | STATS_STORES, 132 | STATS_BRANCHES, 133 | STATS_MUL, 134 | STATS_DIV, 135 | STATS_MAX 136 | }; 137 | uint32_t m_stats[STATS_MAX]; 138 | }; 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /cpu-rv64/rv64.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __RV64_H__ 9 | #define __RV64_H__ 10 | 11 | #include 12 | #include 13 | #include "memory.h" 14 | #include "cpu.h" 15 | 16 | //-------------------------------------------------------------------- 17 | // rv64: RV64IM model 18 | //-------------------------------------------------------------------- 19 | class rv64: public cpu 20 | { 21 | public: 22 | rv64(uint32_t baseAddr = 0, uint32_t len = 0); 23 | 24 | void reset(uint32_t start_addr); 25 | uint32_t get_opcode(uint64_t pc); 26 | void step(void); 27 | 28 | void set_interrupt(int irq); 29 | void clr_interrupt(int irq); 30 | 31 | uint32_t get_register(int r); 32 | uint64_t get_register64(int r); 33 | 34 | uint32_t get_pc(void) { return m_pc_x; } 35 | uint64_t get_pc64(void) { return m_pc_x; } 36 | uint32_t get_opcode(void) { return get_opcode(m_pc_x); } 37 | int get_num_reg(void) { return 32; } 38 | int get_reg_width(void) { return 64; } 39 | 40 | void set_register(int r, uint32_t val); 41 | void set_register(int r, uint64_t val); 42 | void set_pc(uint32_t val); 43 | void set_pc(uint64_t val); 44 | 45 | void stats_reset(void); 46 | void stats_dump(void); 47 | 48 | void enable_mem_unaligned(bool en) { m_enable_unaligned = en; } 49 | void enable_mem_errors(bool en) { m_enable_mem_errors = en; } 50 | void enable_compliant_csr(bool en) { m_compliant_csr = en; } 51 | 52 | // First register for args in ABI 53 | int get_abi_reg_arg0(void) { return 10; } 54 | 55 | // Number of registers (max) used for args 56 | int get_abi_reg_num(void) { return 8; } 57 | 58 | // Enable / Disable ISA extensions 59 | void enable_rvm(bool en) { m_enable_rvm = en; } 60 | void enable_rvc(bool en) { m_enable_rvc = en; } 61 | void enable_rva(bool en) { m_enable_rva = en; } 62 | 63 | // SBI hosting support 64 | void set_timer(uint64_t value); 65 | bool in_super_mode(void); 66 | void sbi_boot(uint32_t boot_addr, uint32_t dtb_addr); 67 | 68 | enum eStats 69 | { 70 | STATS_MIN, 71 | STATS_INSTRUCTIONS = STATS_MIN, 72 | STATS_LOADS, 73 | STATS_STORES, 74 | STATS_BRANCHES, 75 | STATS_MAX 76 | }; 77 | 78 | protected: 79 | bool execute(void); 80 | int load(uint64_t pc, uint64_t address, uint64_t *result, int width, bool signedLoad); 81 | int store(uint64_t pc, uint64_t address, uint64_t data, int width); 82 | virtual bool access_csr(uint64_t address, uint64_t data, bool set, bool clr, uint64_t &result); 83 | void exception(uint64_t cause, uint64_t pc, uint64_t badaddr = 0); 84 | 85 | // MMU 86 | private: 87 | void mmu_flush(void); 88 | int mmu_read_word(uint64_t address, uint64_t *val); 89 | uint64_t mmu_walk(uint64_t addr); 90 | int mmu_i_translate(uint64_t addr, uint64_t *physical); 91 | int mmu_d_translate(uint64_t pc, uint64_t addr, uint64_t *physical, int writeNotRead); 92 | 93 | private: 94 | 95 | // CPU Registers 96 | uint64_t m_gpr[32]; 97 | uint64_t m_pc; 98 | uint64_t m_pc_x; 99 | uint64_t m_load_res; 100 | 101 | // CSR - Machine 102 | uint64_t m_csr_mepc; 103 | uint64_t m_csr_mcause; 104 | uint64_t m_csr_msr; 105 | uint64_t m_csr_mpriv; 106 | uint64_t m_csr_mevec; 107 | uint64_t m_csr_mtval; 108 | uint64_t m_csr_mie; 109 | uint64_t m_csr_mip; 110 | uint64_t m_csr_mtime; 111 | uint64_t m_csr_mtimecmp; 112 | bool m_csr_mtime_ie; 113 | uint64_t m_csr_mscratch; 114 | uint64_t m_csr_mideleg; 115 | uint64_t m_csr_medeleg; 116 | 117 | // CSR - Supervisor 118 | uint64_t m_csr_sepc; 119 | uint64_t m_csr_sevec; 120 | uint64_t m_csr_scause; 121 | uint64_t m_csr_stval; 122 | uint64_t m_csr_satp; 123 | uint64_t m_csr_sscratch; 124 | 125 | // TLB cache 126 | static const int MMU_TLB_ENTRIES = 64; 127 | uint64_t m_mmu_addr[MMU_TLB_ENTRIES]; 128 | uint64_t m_mmu_pte[MMU_TLB_ENTRIES]; 129 | 130 | // Settings 131 | bool m_enable_unaligned; 132 | bool m_enable_mem_errors; 133 | bool m_compliant_csr; 134 | bool m_enable_rvm; 135 | bool m_enable_rvc; 136 | bool m_enable_rva; 137 | bool m_enable_mtimecmp; 138 | bool m_enable_sbi; 139 | 140 | // Stats 141 | uint32_t m_stats[STATS_MAX]; 142 | }; 143 | 144 | #endif 145 | -------------------------------------------------------------------------------- /device-tree/device_tree.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | extern "C" 16 | { 17 | #include 18 | } 19 | 20 | #include "device_tree.h" 21 | 22 | #include "device_uart_lite.h" 23 | #include "device_uart_8250.h" 24 | #include "device_spi_lite.h" 25 | #include "device_timer_r5.h" 26 | #include "device_timer_owl.h" 27 | #include "device_timer_clint.h" 28 | #include "device_irq_ctrl.h" 29 | #include "device_irq_plic.h" 30 | #ifdef INCLUDE_SCREEN 31 | #include "device_frame_buffer.h" 32 | #endif 33 | #include "device_dummy.h" 34 | #include "virtio.h" 35 | 36 | //----------------------------------------------------------------- 37 | // Constructor 38 | //----------------------------------------------------------------- 39 | device_tree::device_tree(const char *filename, console_io *con_io /*= NULL*/) 40 | { 41 | m_fdt = NULL; 42 | m_filename = std::string(filename); 43 | m_console = con_io; 44 | m_mem_base = 0; 45 | m_mem_size = 0; 46 | m_initrd_base = 0; 47 | m_initrd_end = 0; 48 | } 49 | //----------------------------------------------------------------- 50 | // open_fdt: Open FDT file (binary) 51 | //----------------------------------------------------------------- 52 | bool device_tree::open_fdt(void) 53 | { 54 | uint8_t *fdt = NULL; 55 | FILE *f = fopen(m_filename.c_str(), "rb"); 56 | if (f) 57 | { 58 | int size; 59 | 60 | // Get size 61 | fseek(f, 0, SEEK_END); 62 | size = (int)ftell(f); 63 | rewind(f); 64 | 65 | // Read file into buffer 66 | fdt = new uint8_t[size]; 67 | if (fdt) 68 | { 69 | int res = fread(fdt, 1, size, f); 70 | fclose(f); 71 | 72 | if (res != size) 73 | { 74 | delete fdt; 75 | return false; 76 | } 77 | } 78 | } 79 | else 80 | return false; 81 | 82 | if (fdt_check_header(fdt) < 0) 83 | { 84 | fprintf(stderr, "ERROR: Bad DTS format\n"); 85 | delete fdt; 86 | return false; 87 | } 88 | 89 | m_fdt = fdt; 90 | 91 | return true; 92 | } 93 | //----------------------------------------------------------------- 94 | // load: Process device tree 95 | //----------------------------------------------------------------- 96 | bool device_tree::load(cpu *cpu) 97 | { 98 | device *irq_ctrl = NULL; 99 | 100 | printf("Parsing device tree:\n"); 101 | if (open_fdt()) 102 | { 103 | for (int offset = 0 ; offset >= 0; offset = fdt_next_node(m_fdt, offset, NULL)) 104 | { 105 | //const char *name; 106 | //if ((name = fdt_get_name(m_fdt, offset, NULL)) != NULL) 107 | // ; 108 | 109 | int size; 110 | 111 | // Find memory definitions 112 | const char *device_type; 113 | if (device_type = (const char *)fdt_getprop(m_fdt, offset, "device_type", &size)) 114 | { 115 | if (!strcmp(device_type, "memory")) 116 | { 117 | const uint32_t *reg = (const uint32_t*)fdt_getprop(m_fdt, offset, "reg", &size); 118 | if (!reg) 119 | continue; 120 | 121 | assert(size == 8); 122 | 123 | uint32_t reg_addr = ntohl(reg[0]); 124 | uint32_t reg_size = ntohl(reg[1]); 125 | 126 | printf("|- Attach memory: Addr %08x - %08x\n", reg_addr, reg_addr + reg_size - 1); 127 | cpu->create_memory(reg_addr, reg_size); 128 | m_mem_base = reg_addr; 129 | m_mem_size = reg_size; 130 | } 131 | else if (!strcmp(device_type, "cpu")) 132 | { 133 | continue; 134 | } 135 | } 136 | 137 | // Find initrd params 138 | { 139 | const uint32_t *reg = (const uint32_t*)fdt_getprop(m_fdt, offset, "linux,initrd-start", &size); 140 | if (reg) 141 | m_initrd_base = ntohl(reg[0]); 142 | 143 | reg = (const uint32_t*)fdt_getprop(m_fdt, offset, "linux,initrd-end", &size); 144 | if (reg) 145 | m_initrd_end = ntohl(reg[0]); 146 | } 147 | 148 | // Match device 149 | const char * compat; 150 | if (compat = (const char *)fdt_getprop(m_fdt, offset, "compatible", &size)) 151 | { 152 | const uint32_t *reg = (const uint32_t*)fdt_getprop(m_fdt, offset, "reg", &size); 153 | if (!reg) 154 | continue; 155 | 156 | uint32_t reg_addr = ntohl(reg[0]); 157 | uint32_t reg_size = (size > 4) ? ntohl(reg[1]) : 0; 158 | 159 | int irq_num = -1; 160 | const uint32_t *irq = (const uint32_t*)fdt_getprop(m_fdt, offset, "interrupts", &size); 161 | if (irq) 162 | irq_num = ntohl(*irq); 163 | 164 | if (!strcmp(compat, "xlnx,xps-intc-1.00.a")) 165 | { 166 | // Create IRQ controller 167 | // TODO: Support multiple controllers 168 | irq_ctrl = new device_irq_ctrl(reg_addr, 11); 169 | printf("|- Create interrupt controller (Xilinx): Addr %08x\n", reg_addr); 170 | cpu->attach_device(irq_ctrl); 171 | } 172 | else if (!strcmp(compat, "riscv,plic0") || !strcmp(compat, "sifive,plic-1.0.0")) 173 | { 174 | irq_ctrl = new device_irq_plic(reg_addr, 11); 175 | printf("|- Create interrupt controller (PLIC): Addr %08x\n", reg_addr); 176 | cpu->attach_device(irq_ctrl); 177 | } 178 | else if (!strcmp(compat, "xlnx,xps-uartlite-1.00.a")) 179 | { 180 | printf("|- Create UART: Addr %08x IRQ %d\n", reg_addr, irq_num); 181 | cpu->attach_device(new device_uart_lite(reg_addr, irq_ctrl, irq_num, m_console)); 182 | } 183 | else if (!strcmp(compat, "ns8250")) 184 | { 185 | printf("|- Create UART: Addr %08x IRQ %d\n", reg_addr, irq_num); 186 | cpu->attach_device(new device_uart_8250(reg_addr, irq_ctrl, irq_num, m_console)); 187 | } 188 | else if (!strcmp(compat, "actions,s500-timer")) 189 | { 190 | printf("|- Create Timer: Addr %08x IRQ %d\n", reg_addr, irq_num); 191 | cpu->attach_device(new device_timer_owl(reg_addr, irq_ctrl, irq_num)); 192 | } 193 | else if (!strcmp(compat, "riscv,openr5-timer")) 194 | { 195 | printf("|- Create Timer: Addr %08x IRQ %d\n", reg_addr, irq_num); 196 | cpu->attach_device(new device_timer_r5(reg_addr, irq_ctrl, irq_num)); 197 | } 198 | else if (!strcmp(compat, "riscv,clint0")) 199 | { 200 | // TODO: dual irq numbers... 201 | printf("|- Create Timer: Addr %08x IRQ %d\n", reg_addr, irq_num); 202 | cpu->attach_device(new device_timer_clint(reg_addr, cpu)); 203 | } 204 | else if (!strcmp(compat, "xlnx,xps-spi-2.00.b")) 205 | { 206 | printf("|- Create SPI: Addr %08x IRQ %d\n", reg_addr, irq_num); 207 | cpu->attach_device(new device_spi_lite(reg_addr, irq_ctrl, irq_num)); 208 | } 209 | #ifdef INCLUDE_SCREEN 210 | else if (!strcmp(compat, "simple-framebuffer")) 211 | { 212 | uint32_t width = 640; 213 | uint32_t height = 480; 214 | const uint32_t *p_width = (const uint32_t*)fdt_getprop(m_fdt, offset, "width", &size); 215 | if (p_width) 216 | width = ntohl(*p_width); 217 | const uint32_t *p_height = (const uint32_t*)fdt_getprop(m_fdt, offset, "height", &size); 218 | if (p_height) 219 | height = ntohl(*p_height); 220 | 221 | printf("|- Create frame buffer: Addr %08x IRQ %d\n", reg_addr, irq_num); 222 | cpu->attach_device(new device_frame_buffer(reg_addr, width, height)); 223 | } 224 | #endif 225 | else if (!strcmp(compat, "virtio,mmio")) 226 | { 227 | printf("|- Create VirtIO: Addr %08x IRQ %d\n", reg_addr, irq_num); 228 | cpu->attach_device(new virtio(cpu, reg_addr, irq_ctrl, irq_num)); 229 | } 230 | else 231 | { 232 | printf("|- Create dummy device (%s): Addr %08x - %08x\n", compat, reg_addr, reg_addr + reg_size-1); 233 | cpu->attach_device(new device_dummy(reg_addr, reg_size)); 234 | } 235 | } 236 | } 237 | 238 | return true; 239 | } 240 | 241 | return false; 242 | } -------------------------------------------------------------------------------- /device-tree/device_tree.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_TREE_H__ 9 | #define __DEVICE_TREE_H__ 10 | 11 | #include 12 | #include 13 | 14 | #include "memory.h" 15 | #include "device.h" 16 | #include "console_io.h" 17 | 18 | // Forward decl 19 | class cpu; 20 | 21 | //-------------------------------------------------------------------- 22 | // ELF loader 23 | //-------------------------------------------------------------------- 24 | class device_tree 25 | { 26 | public: 27 | device_tree(const char *filename, console_io *con_io = NULL); 28 | 29 | bool load(cpu *cpu); 30 | 31 | // This API only makes sense for systems with a single linear memory 32 | uint32_t get_mem_base(void) { return m_mem_base; } 33 | uint32_t get_mem_size(void) { return m_mem_size; } 34 | 35 | uint32_t get_initrd_base(void) { return m_initrd_base; } 36 | uint32_t get_initrd_size(void) { return m_initrd_end - m_initrd_base; } 37 | 38 | protected: 39 | bool open_fdt(void); 40 | int process_node(int offset); 41 | 42 | protected: 43 | std::string m_filename; 44 | uint8_t * m_fdt; 45 | console_io *m_console; 46 | uint32_t m_mem_base; 47 | uint32_t m_mem_size; 48 | uint32_t m_initrd_base; 49 | uint32_t m_initrd_end; 50 | }; 51 | 52 | #endif -------------------------------------------------------------------------------- /display/display.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include "display.h" 9 | 10 | #ifdef INCLUDE_SCREEN 11 | #include 12 | #endif 13 | 14 | //----------------------------------------------------------------- 15 | // Defines 16 | //----------------------------------------------------------------- 17 | // RGB565 pixel format -> components 18 | #define RGB16_R(value) ((((value) >> 11) & 0x1F) << 3) 19 | #define RGB16_G(value) ((((value) >> 5) & 0x3F) << 2) 20 | #define RGB16_B(value) ((((value) >> 0) & 0x1F) << 3) 21 | 22 | //----------------------------------------------------------------- 23 | // init: Create SDL based screen buffer 24 | //----------------------------------------------------------------- 25 | bool display::init(int width, int height) 26 | { 27 | #ifdef INCLUDE_SCREEN 28 | m_width = width; 29 | m_height = height; 30 | 31 | if (SDL_Init (SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE)) 32 | return false; 33 | 34 | int flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL; 35 | SDL_Surface *screen = SDL_SetVideoMode(m_width, m_height, 0, flags); 36 | if (!screen || !screen->pixels) 37 | return false; 38 | 39 | m_screen = screen; 40 | SDL_WM_SetCaption("ExactStep", "ExactStep"); 41 | 42 | // Disable cursor 43 | uint8_t data = 0; 44 | SDL_Cursor *cursor = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); 45 | SDL_ShowCursor(1); 46 | SDL_SetCursor(cursor); 47 | #endif 48 | 49 | return true; 50 | } 51 | //----------------------------------------------------------------- 52 | // update: Redraw entire screen from frame buffer 53 | //----------------------------------------------------------------- 54 | bool display::update(uint8_t *memory) 55 | { 56 | #ifdef INCLUDE_SCREEN 57 | SDL_Surface *screen = (SDL_Surface *)m_screen; 58 | uint16_t *frame = (uint16_t *)memory; 59 | 60 | SDL_LockSurface(screen); 61 | for (int y=0;ypixels)[(y * (screen->pitch / 4)) + x] = 71 | ( 72 | r << screen->format->Rshift | 73 | g << screen->format->Gshift | 74 | b << screen->format->Bshift 75 | ); 76 | } 77 | } 78 | SDL_UnlockSurface(screen); 79 | SDL_UpdateRect(screen, 0, 0, 0, 0); 80 | #endif 81 | 82 | return true; 83 | } 84 | 85 | 86 | -------------------------------------------------------------------------------- /display/display.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DISPLAY_H__ 9 | #define __DISPLAY_H__ 10 | 11 | #include 12 | #include 13 | 14 | //----------------------------------------------------------------- 15 | // SDL based display widget 16 | //----------------------------------------------------------------- 17 | class display 18 | { 19 | public: 20 | display() 21 | { 22 | m_screen = NULL; 23 | m_width = 0; 24 | m_height = 0; 25 | } 26 | bool init(int width, int height); 27 | bool update(uint8_t *memory); 28 | 29 | private: 30 | int m_width; 31 | int m_height; 32 | void *m_screen; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /docs/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ultraembedded/exactstep/8a0528c7d24124907bcc81f8363eb0ab36d7a8e3/docs/screenshot.png -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | ## Simulator Makefile 3 | ############################################################################### 4 | 5 | # TARGETS 6 | TARGETS ?= exactstep exactstep-riscv-linux 7 | 8 | HAS_SCREEN ?= False 9 | HAS_NETWORK ?= False 10 | 11 | # Source Files 12 | SRC_DIR = core peripherals cpu-rv32 cpu-rv64 cpu-armv6m cpu-mips-i cli platforms device-tree display net virtio sbi 13 | 14 | CFLAGS = -O2 -fPIC 15 | CFLAGS += -Wno-format 16 | ifneq ($(HAS_NETWORK),False) 17 | CFLAGS += -DINCLUDE_NET_DEVICE 18 | endif 19 | ifneq ($(HAS_SCREEN),False) 20 | CFLAGS += -DINCLUDE_SCREEN 21 | endif 22 | 23 | INCLUDE_PATH += $(SRC_DIR) 24 | CFLAGS += $(patsubst %,-I%,$(INCLUDE_PATH)) 25 | 26 | LDFLAGS = 27 | LIBS = -lelf -lbfd -lfdt 28 | 29 | ifneq ($(HAS_SCREEN),False) 30 | LIBS += -lSDL 31 | endif 32 | 33 | ############################################################################### 34 | # Variables 35 | ############################################################################### 36 | OBJ_DIR ?= obj/ 37 | 38 | ############################################################################### 39 | # Variables: Lists of objects, source and deps 40 | ############################################################################### 41 | # SRC / Object list 42 | src2obj = $(OBJ_DIR)$(patsubst %$(suffix $(1)),%.o,$(notdir $(1))) 43 | 44 | SRC ?= $(foreach src,$(SRC_DIR),$(wildcard $(src)/*.cpp)) 45 | SRC_FILT := $(filter-out cli/main.cpp,$(SRC)) 46 | SRC_FILT := $(filter-out cli/main_riscv_linux.cpp,$(SRC_FILT)) 47 | 48 | OBJ ?= $(foreach src,$(SRC_FILT),$(call src2obj,$(src))) 49 | 50 | ############################################################################### 51 | # Rules: Compilation macro 52 | ############################################################################### 53 | define template_cpp 54 | $(call src2obj,$(1)): $(1) | $(OBJ_DIR) 55 | @echo "# Compiling $(notdir $(1))" 56 | @g++ $(CFLAGS) -c $$< -o $$@ 57 | endef 58 | 59 | ############################################################################### 60 | # Rules 61 | ############################################################################### 62 | all: $(TARGETS) 63 | 64 | $(OBJ_DIR): 65 | @mkdir -p $@ 66 | 67 | $(foreach src,$(SRC),$(eval $(call template_cpp,$(src)))) 68 | 69 | exactstep: $(OBJ) $(OBJ_DIR)main.o makefile 70 | @echo "# Linking $(notdir $@)" 71 | @g++ $(LDFLAGS) $(OBJ_DIR)main.o $(OBJ) $(LIBS) -o $@ 72 | 73 | exactstep-riscv-linux: $(OBJ) $(OBJ_DIR)main_riscv_linux.o makefile 74 | @echo "# Linking $(notdir $@)" 75 | @g++ $(LDFLAGS) $(OBJ_DIR)main_riscv_linux.o $(OBJ) $(LIBS) -o $@ 76 | 77 | clean: 78 | -rm -rf $(OBJ_DIR) $(TARGETS) 79 | 80 | -------------------------------------------------------------------------------- /net/net_device.h: -------------------------------------------------------------------------------- 1 | #ifndef __NET_DEVICE_H__ 2 | #define __NET_DEVICE_H__ 3 | 4 | #include 5 | 6 | class net_device 7 | { 8 | public: 9 | virtual int receive(uint8_t *buffer, int max_len) = 0; 10 | virtual int send(uint8_t *buffer, int length) = 0; 11 | }; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /net/net_tap.cpp: -------------------------------------------------------------------------------- 1 | #include "net_tap.h" 2 | 3 | #ifdef INCLUDE_NET_DEVICE 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | //------------------------------------------------------------ 18 | // Constructor 19 | //------------------------------------------------------------ 20 | net_tap::net_tap(const char *if_name) 21 | { 22 | m_fd = -1; 23 | init(if_name); 24 | } 25 | //------------------------------------------------------------ 26 | // init: Intialise TAP based ethernet driver 27 | //------------------------------------------------------------ 28 | bool net_tap::init(const char *if_name) 29 | { 30 | struct ifreq ifr; 31 | if ((m_fd = open("/dev/net/tun", O_RDWR | O_NONBLOCK)) < 0) 32 | { 33 | perror("Opening /dev/net/tun"); 34 | return false; 35 | } 36 | 37 | memset(&ifr, 0, sizeof(ifr)); 38 | ifr.ifr_flags = IFF_TAP | IFF_NO_PI; 39 | strncpy(ifr.ifr_name, if_name, IFNAMSIZ); 40 | 41 | if ((ioctl(m_fd, TUNSETIFF, (void *)&ifr)) < 0) 42 | { 43 | fprintf(stderr, "ERROR: Cannot open TAP device '%s' (permissions?)\n", if_name); 44 | close(m_fd); 45 | m_fd = -1; 46 | return false; 47 | } 48 | 49 | return true; 50 | } 51 | //------------------------------------------------------------ 52 | // receive: Poll for receive data 53 | //------------------------------------------------------------ 54 | int net_tap::receive(uint8_t *buffer, int max_len) 55 | { 56 | if (m_fd < 0) 57 | return 0; 58 | 59 | int l = read(m_fd, (char*)buffer, max_len); 60 | if (l < 0 && errno == EAGAIN) 61 | return 0; 62 | 63 | if(l < 0) 64 | { 65 | perror("Reading from interface"); 66 | close(m_fd); 67 | return 0; 68 | } 69 | 70 | return l; 71 | } 72 | //------------------------------------------------------------ 73 | // send: Send ethernet packet 74 | //------------------------------------------------------------ 75 | int net_tap::send(uint8_t *buffer, int length) 76 | { 77 | if (m_fd < 0) 78 | return 0; 79 | 80 | return write(m_fd, buffer, length) == length; 81 | } 82 | #endif 83 | -------------------------------------------------------------------------------- /net/net_tap.h: -------------------------------------------------------------------------------- 1 | #ifndef __NET_TAP_H__ 2 | #define __NET_TAP_H__ 3 | 4 | #include "net_device.h" 5 | 6 | class net_tap: public net_device 7 | { 8 | public: 9 | net_tap(const char *if_name); 10 | bool init(const char *if_name); 11 | int receive(uint8_t *buffer, int max_len); 12 | int send(uint8_t *buffer, int length); 13 | 14 | protected: 15 | int m_fd; 16 | }; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /peripherals/device_dummy.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_DUMMY_H__ 9 | #define __DEVICE_DUMMY_H__ 10 | 11 | #include "device.h" 12 | 13 | //----------------------------------------------------------------- 14 | // device_dummy: 15 | //----------------------------------------------------------------- 16 | class device_dummy: public device 17 | { 18 | public: 19 | device_dummy(uint32_t base_addr, uint32_t size): device("dummy", base_addr, size, NULL, -1) 20 | { 21 | reset(); 22 | } 23 | 24 | bool write32(uint32_t address, uint32_t data) 25 | { 26 | printf("DUMMY (WR): %08x=%08x\n", address, data); 27 | return true; 28 | } 29 | bool read32(uint32_t address, uint32_t &data) 30 | { 31 | data = 0; 32 | printf("DUMMY (RD): %08x=%08x\n", address, data); 33 | return true; 34 | } 35 | 36 | int clock(void) 37 | { 38 | return 0; 39 | } 40 | 41 | private: 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /peripherals/device_frame_buffer.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_FRAME_BUFFER_H__ 9 | #define __DEVICE_FRAME_BUFFER_H__ 10 | 11 | #include "device.h" 12 | #include "display.h" 13 | 14 | //----------------------------------------------------------------- 15 | // device_frame_buffer: Simplified frame buffer device 16 | //----------------------------------------------------------------- 17 | class device_frame_buffer: public device 18 | { 19 | public: 20 | device_frame_buffer(uint32_t base_addr, int width, int height): device("fb", base_addr, height * width * 2, NULL, -1) 21 | { 22 | m_fb = new uint8_t[height * width * 2]; 23 | m_ticks = 0; 24 | 25 | m_display.init(width, height); 26 | } 27 | 28 | void reset(void) 29 | { 30 | 31 | } 32 | 33 | int min_access_size(void) { return 1; } 34 | 35 | bool write8(uint32_t address, uint8_t data) 36 | { 37 | address -= m_base; 38 | m_fb[address] = data; 39 | return true; 40 | } 41 | 42 | bool write_block(uint32_t address, uint8_t *data, int length) 43 | { 44 | address -= m_base; 45 | for (int i=0;i> 0; 54 | m_fb[address+1] = data >> 8; 55 | m_fb[address+2] = data >> 16; 56 | m_fb[address+3] = data >> 24; 57 | return true; 58 | } 59 | 60 | bool read32(uint32_t address, uint32_t &data) 61 | { 62 | return true; 63 | } 64 | 65 | int clock(void) 66 | { 67 | if (++m_ticks == 100000) 68 | { 69 | m_display.update(m_fb); 70 | m_ticks = 0; 71 | } 72 | return 0; 73 | } 74 | 75 | private: 76 | uint8_t *m_fb; 77 | int m_ticks; 78 | display m_display; 79 | }; 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /peripherals/device_irq_ctrl.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_IRQ_CTRL_H__ 9 | #define __DEVICE_IRQ_CTRL_H__ 10 | 11 | #include "device.h" 12 | 13 | //----------------------------------------------------------------- 14 | // Defines 15 | //----------------------------------------------------------------- 16 | #define IRQ_ISR 0x0 17 | #define IRQ_ISR_STATUS_SHIFT 0 18 | #define IRQ_ISR_STATUS_MASK 0x3 19 | 20 | #define IRQ_IPR 0x4 21 | #define IRQ_IPR_PENDING_SHIFT 0 22 | #define IRQ_IPR_PENDING_MASK 0x3 23 | 24 | #define IRQ_IER 0x8 25 | #define IRQ_IER_ENABLE_SHIFT 0 26 | #define IRQ_IER_ENABLE_MASK 0x3 27 | 28 | #define IRQ_IAR 0xc 29 | #define IRQ_IAR_ACK_SHIFT 0 30 | #define IRQ_IAR_ACK_MASK 0x3 31 | 32 | #define IRQ_SIE 0x10 33 | #define IRQ_SIE_SET_SHIFT 0 34 | #define IRQ_SIE_SET_MASK 0x3 35 | 36 | #define IRQ_CIE 0x14 37 | #define IRQ_CIE_CLR_SHIFT 0 38 | #define IRQ_CIE_CLR_MASK 0x3 39 | 40 | #define IRQ_IVR 0x18 41 | #define IRQ_IVR_VECTOR_SHIFT 0 42 | #define IRQ_IVR_VECTOR_MASK 0xffffffff 43 | 44 | #define IRQ_MER 0x1c 45 | #define IRQ_MER_ME 0 46 | #define IRQ_MER_ME_SHIFT 0 47 | #define IRQ_MER_ME_MASK 0x3 48 | 49 | //----------------------------------------------------------------- 50 | // device_irq_ctrl: Basic IRQ controller somewhat compatible with Xilinx IP 51 | //----------------------------------------------------------------- 52 | class device_irq_ctrl: public device 53 | { 54 | public: 55 | device_irq_ctrl(uint32_t base_addr, int irq): device("irq_ctrl", base_addr, 256, NULL, irq) 56 | { 57 | reset(); 58 | } 59 | 60 | void reset(void) 61 | { 62 | m_isr = 0; 63 | m_ier = 0; 64 | m_mer = 0; 65 | m_irq_last = false; 66 | } 67 | 68 | void eval_irq(void) 69 | { 70 | bool irq = ((m_isr & m_ier) != 0 && m_mer); 71 | 72 | if (irq && !m_irq_last) 73 | raise_interrupt(); 74 | else if (!irq && m_irq_last) 75 | drop_interrupt(); 76 | 77 | m_irq_last = irq; 78 | } 79 | 80 | void set_irq(int irq) 81 | { 82 | if (irq != -1) 83 | m_isr |= (1 << irq); 84 | 85 | eval_irq(); 86 | } 87 | 88 | bool write32(uint32_t address, uint32_t data) 89 | { 90 | address -= m_base; 91 | switch (address) 92 | { 93 | case IRQ_ISR: 94 | m_isr |= data; 95 | break; 96 | case IRQ_IER: 97 | m_ier = data; 98 | break; 99 | case IRQ_SIE: 100 | m_ier|= data; 101 | break; 102 | case IRQ_CIE: 103 | m_ier&= ~data; 104 | break; 105 | case IRQ_IAR: 106 | m_isr&= ~data; 107 | break; 108 | case IRQ_MER: 109 | m_mer = data & (IRQ_MER_ME_MASK << IRQ_MER_ME_SHIFT); 110 | break; 111 | default: 112 | fprintf(stderr, "IRQ-CTRL: Bad write @ %08x\n", address); 113 | return false; 114 | break; 115 | } 116 | eval_irq(); 117 | 118 | return true; 119 | } 120 | bool read32(uint32_t address, uint32_t &data) 121 | { 122 | data = 0; 123 | address -= m_base; 124 | switch (address) 125 | { 126 | case IRQ_ISR: 127 | data = m_isr; 128 | break; 129 | case IRQ_IPR: 130 | data = m_isr & m_ier; 131 | break; 132 | case IRQ_IER: 133 | data = m_ier; 134 | break; 135 | case IRQ_IVR: 136 | data = (uint32_t)-1; 137 | for (int i=0;i<32;i++) 138 | if ((m_isr & m_ier) & (1 << i)) 139 | { 140 | data = i; 141 | break; 142 | } 143 | break; 144 | case IRQ_MER: 145 | data = m_mer; 146 | break; 147 | default: 148 | fprintf(stderr, "IRQ-CTRL: Bad read @ %08x\n", address); 149 | return false; 150 | break; 151 | } 152 | return true; 153 | } 154 | 155 | int clock(void) 156 | { 157 | return 0; 158 | } 159 | 160 | private: 161 | uint32_t m_base_addr; 162 | uint32_t m_isr; 163 | uint32_t m_ier; 164 | uint32_t m_mer; 165 | 166 | bool m_irq_last; 167 | }; 168 | 169 | #endif 170 | -------------------------------------------------------------------------------- /peripherals/device_irq_plic.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_IRQ_PLIC_H__ 9 | #define __DEVICE_IRQ_PLIC_H__ 10 | 11 | #include "device.h" 12 | 13 | //----------------------------------------------------------------- 14 | // Defines 15 | //----------------------------------------------------------------- 16 | #define PLIC_REG_SRC_PRIO0 0x0000 17 | #define PLIC_REG_SRC_PRIO127 0x01fc 18 | #define PLIC_REG_PENDING0 0x1000 19 | #define PLIC_REG_PENDING3 0x100c 20 | #define PLIC_REG_ENABLE0 0x2000 21 | #define PLIC_REG_ENABLE3 0x200c 22 | #define PLIC_ENABLE_PER_HART 0x80 23 | #define PLIC_REG_PRIO_THRESH 0x00200000 24 | #define PLIC_REG_CLAIM 0x00200004 25 | #define PLIC_CONTEXT_PER_HART 0x1000 26 | 27 | #define PLIC_NUM_IRQS 128 28 | #define PLIC_REG_SIZE (0x10000000 - 0x0c000000) 29 | #define PLIC_IRQ_GROUPS 4 30 | 31 | //----------------------------------------------------------------- 32 | // device_irq_plic: 'Platform Interrupt Controller' model 33 | //----------------------------------------------------------------- 34 | class device_irq_plic: public device 35 | { 36 | public: 37 | device_irq_plic(uint32_t base_addr, int irq): device("plic", base_addr, PLIC_REG_SIZE, NULL, irq) 38 | { 39 | reset(); 40 | } 41 | 42 | void reset(void) 43 | { 44 | for (int x=0;x= PLIC_REG_SRC_PRIO0 && address <= PLIC_REG_SRC_PRIO127) 106 | m_prio[(address-PLIC_REG_SRC_PRIO0)/4] = data; 107 | else if (address >= PLIC_REG_ENABLE0 && address <= PLIC_REG_ENABLE3) 108 | m_enable[(address-PLIC_REG_ENABLE0)/4] = data; 109 | // Context 1 (S) aliases to context 0 (M) in this implementation 110 | else if (address >= (PLIC_REG_ENABLE0 + PLIC_ENABLE_PER_HART) && address <= (PLIC_REG_ENABLE3 + PLIC_ENABLE_PER_HART)) 111 | m_enable[(address-PLIC_REG_ENABLE0-PLIC_ENABLE_PER_HART)/4] = data; 112 | else if ((address == PLIC_REG_PRIO_THRESH) || (address == PLIC_REG_PRIO_THRESH + PLIC_CONTEXT_PER_HART)) 113 | m_prio_thresh = data; 114 | // Complete interrupt 115 | else if ((address == PLIC_REG_CLAIM) || (address == (PLIC_REG_CLAIM + PLIC_CONTEXT_PER_HART))) 116 | { 117 | // Interrupt claimed - clear 118 | if (data > 0) 119 | { 120 | int grp = data / 32; 121 | int bit = data % 32; 122 | m_pending[grp] &= ~(1 << bit); 123 | } 124 | 125 | // Drop interrupt if nothing new pending 126 | if (eval_irq() == 0) 127 | drop_interrupt(); 128 | } 129 | else 130 | fprintf(stderr, "PLIC: Bad write @ %08x\n", address); 131 | 132 | eval_irq(); 133 | return true; 134 | } 135 | 136 | bool read32(uint32_t address, uint32_t &data) 137 | { 138 | data = 0; 139 | address -= m_base; 140 | 141 | if (address >= PLIC_REG_SRC_PRIO0 && address <= PLIC_REG_SRC_PRIO127) 142 | { 143 | data = m_prio[(address-PLIC_REG_SRC_PRIO0)/4]; 144 | return true; 145 | } 146 | else if (address >= PLIC_REG_PENDING0 && address <= PLIC_REG_PENDING3) 147 | { 148 | data = m_pending[(address-PLIC_REG_PENDING0)/4]; 149 | return true; 150 | } 151 | else if (address >= PLIC_REG_ENABLE0 && address <= PLIC_REG_ENABLE3) 152 | { 153 | data = m_enable[(address-PLIC_REG_ENABLE0)/4]; 154 | return true; 155 | } 156 | // Context 1 (S) aliases to context 0 (M) in this implementation 157 | else if (address >= (PLIC_REG_ENABLE0 + PLIC_ENABLE_PER_HART) && address <= (PLIC_REG_ENABLE3 + PLIC_ENABLE_PER_HART)) 158 | { 159 | data = m_enable[(address-PLIC_REG_ENABLE0 - PLIC_ENABLE_PER_HART)/4]; 160 | return true; 161 | } 162 | else if ((address == PLIC_REG_PRIO_THRESH) || (address == PLIC_REG_PRIO_THRESH + PLIC_CONTEXT_PER_HART)) 163 | { 164 | data = m_prio_thresh; 165 | return true; 166 | } 167 | else if ((address == PLIC_REG_CLAIM) || (address == (PLIC_REG_CLAIM + PLIC_CONTEXT_PER_HART))) 168 | { 169 | int irq = eval_irq(); 170 | data = (uint32_t)irq; 171 | return true; 172 | } 173 | 174 | fprintf(stderr, "PLIC: Bad read @ %08x\n", address); 175 | return false; 176 | } 177 | 178 | int clock(void) 179 | { 180 | return 0; 181 | } 182 | 183 | private: 184 | uint8_t m_prio[PLIC_NUM_IRQS]; 185 | uint32_t m_pending[PLIC_IRQ_GROUPS]; 186 | uint32_t m_enable[PLIC_IRQ_GROUPS]; 187 | uint8_t m_prio_thresh; 188 | bool m_irq; 189 | }; 190 | 191 | #endif 192 | -------------------------------------------------------------------------------- /peripherals/device_spi_lite.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_SPI_LITE_H__ 9 | #define __DEVICE_SPI_LITE_H__ 10 | 11 | #include 12 | #include "device.h" 13 | 14 | #define dprintf //printf 15 | 16 | //----------------------------------------------------------------- 17 | // Defines 18 | //----------------------------------------------------------------- 19 | #define SPI_FIFO_DEPTH 4 20 | #define SPI_CLOCK_DIV 32 21 | 22 | //----------------------------------------------------------------- 23 | // Defines 24 | //----------------------------------------------------------------- 25 | #define SPI_DGIER 0x1c 26 | #define SPI_DGIER_GIE 31 27 | #define SPI_DGIER_GIE_SHIFT 31 28 | #define SPI_DGIER_GIE_MASK 0x1 29 | 30 | #define SPI_IPISR 0x20 31 | #define SPI_IPISR_TX_EMPTY 2 32 | #define SPI_IPISR_TX_EMPTY_SHIFT 2 33 | #define SPI_IPISR_TX_EMPTY_MASK 0x1 34 | 35 | #define SPI_IPIER 0x28 36 | #define SPI_IPIER_TX_EMPTY 2 37 | #define SPI_IPIER_TX_EMPTY_SHIFT 2 38 | #define SPI_IPIER_TX_EMPTY_MASK 0x1 39 | 40 | #define SPI_SRR 0x40 41 | #define SPI_SRR_RESET_SHIFT 0 42 | #define SPI_SRR_RESET_MASK 0xffffffff 43 | 44 | #define SPI_CR 0x60 45 | #define SPI_CR_LOOP 0 46 | #define SPI_CR_LOOP_SHIFT 0 47 | #define SPI_CR_LOOP_MASK 0x1 48 | 49 | #define SPI_CR_SPE 1 50 | #define SPI_CR_SPE_SHIFT 1 51 | #define SPI_CR_SPE_MASK 0x1 52 | 53 | #define SPI_CR_MASTER 2 54 | #define SPI_CR_MASTER_SHIFT 2 55 | #define SPI_CR_MASTER_MASK 0x1 56 | 57 | #define SPI_CR_CPOL 3 58 | #define SPI_CR_CPOL_SHIFT 3 59 | #define SPI_CR_CPOL_MASK 0x1 60 | 61 | #define SPI_CR_CPHA 4 62 | #define SPI_CR_CPHA_SHIFT 4 63 | #define SPI_CR_CPHA_MASK 0x1 64 | 65 | #define SPI_CR_TXFIFO_RST 5 66 | #define SPI_CR_TXFIFO_RST_SHIFT 5 67 | #define SPI_CR_TXFIFO_RST_MASK 0x1 68 | 69 | #define SPI_CR_RXFIFO_RST 6 70 | #define SPI_CR_RXFIFO_RST_SHIFT 6 71 | #define SPI_CR_RXFIFO_RST_MASK 0x1 72 | 73 | #define SPI_CR_MANUAL_SS 7 74 | #define SPI_CR_MANUAL_SS_SHIFT 7 75 | #define SPI_CR_MANUAL_SS_MASK 0x1 76 | 77 | #define SPI_CR_TRANS_INHIBIT 8 78 | #define SPI_CR_TRANS_INHIBIT_SHIFT 8 79 | #define SPI_CR_TRANS_INHIBIT_MASK 0x1 80 | 81 | #define SPI_CR_LSB_FIRST 9 82 | #define SPI_CR_LSB_FIRST_SHIFT 9 83 | #define SPI_CR_LSB_FIRST_MASK 0x1 84 | 85 | #define SPI_SR 0x64 86 | #define SPI_SR_RX_EMPTY 0 87 | #define SPI_SR_RX_EMPTY_SHIFT 0 88 | #define SPI_SR_RX_EMPTY_MASK 0x1 89 | 90 | #define SPI_SR_RX_FULL 1 91 | #define SPI_SR_RX_FULL_SHIFT 1 92 | #define SPI_SR_RX_FULL_MASK 0x1 93 | 94 | #define SPI_SR_TX_EMPTY 2 95 | #define SPI_SR_TX_EMPTY_SHIFT 2 96 | #define SPI_SR_TX_EMPTY_MASK 0x1 97 | 98 | #define SPI_SR_TX_FULL 3 99 | #define SPI_SR_TX_FULL_SHIFT 3 100 | #define SPI_SR_TX_FULL_MASK 0x1 101 | 102 | #define SPI_DTR 0x68 103 | #define SPI_DTR_DATA_SHIFT 0 104 | #define SPI_DTR_DATA_MASK 0xff 105 | 106 | #define SPI_DRR 0x6c 107 | #define SPI_DRR_DATA_SHIFT 0 108 | #define SPI_DRR_DATA_MASK 0xff 109 | 110 | #define SPI_SSR 0x70 111 | #define SPI_SSR_VALUE 0 112 | #define SPI_SSR_VALUE_SHIFT 0 113 | #define SPI_SSR_VALUE_MASK 0x1 114 | 115 | //----------------------------------------------------------------- 116 | // SpiLite: Model of Xilinx SPI-Lite IP 117 | //----------------------------------------------------------------- 118 | class device_spi_lite: public device 119 | { 120 | public: 121 | device_spi_lite(uint32_t base_addr, device *irq_ctrl, int irq_num): device("spi_lite", base_addr, 256, irq_ctrl, irq_num) 122 | { 123 | reset(); 124 | } 125 | 126 | void reset(void) 127 | { 128 | memset(&m_reg, 0, 256 * 4); 129 | m_spi_delay = 0; 130 | m_irq_inhibit = false; 131 | } 132 | 133 | void tx_push(uint32_t data) 134 | { 135 | if (m_tx.size() < SPI_FIFO_DEPTH) 136 | m_tx.push(data); 137 | } 138 | 139 | void rx_push(uint32_t data) 140 | { 141 | if (m_rx.size() < SPI_FIFO_DEPTH) 142 | m_rx.push(data); 143 | } 144 | 145 | void tx_flush(void) 146 | { 147 | while (!m_tx.empty()) 148 | m_tx.pop(); 149 | } 150 | 151 | void rx_flush(void) 152 | { 153 | while (!m_rx.empty()) 154 | m_rx.pop(); 155 | } 156 | 157 | bool write32(uint32_t address, uint32_t data) 158 | { 159 | dprintf("SPI: Write %08x=%08x\n", address, data); 160 | address -= m_base; 161 | switch (address) 162 | { 163 | case SPI_DTR: 164 | tx_push(data); 165 | break; 166 | case SPI_CR: 167 | // Tx FIFO flush 168 | if (data & (1 << SPI_CR_TXFIFO_RST_SHIFT)) 169 | { 170 | tx_flush(); 171 | data &= ~(1 << SPI_CR_TXFIFO_RST_SHIFT); 172 | } 173 | // Rx FIFO flush 174 | if (data & (1 << SPI_CR_RXFIFO_RST_SHIFT)) 175 | { 176 | rx_flush(); 177 | data &= ~(1 << SPI_CR_RXFIFO_RST_SHIFT); 178 | } 179 | break; 180 | case SPI_SRR: 181 | // Soft reset 182 | if (data == 0x0000000A) 183 | { 184 | tx_flush(); 185 | rx_flush(); 186 | data = 0; 187 | } 188 | break; 189 | case SPI_IPISR: 190 | data = m_reg[address/4] & ~data; 191 | m_irq_inhibit = false; 192 | break; 193 | } 194 | 195 | m_reg[address/4] = data; 196 | return true; 197 | } 198 | bool read32(uint32_t address, uint32_t &data) 199 | { 200 | address -= m_base; 201 | switch (address) 202 | { 203 | case SPI_DRR: 204 | if (m_rx.size() != 0) 205 | { 206 | m_reg[address/4] = m_rx.front(); 207 | m_rx.pop(); 208 | } 209 | break; 210 | case SPI_SR: 211 | { 212 | bool tx_full = m_tx.size() == SPI_FIFO_DEPTH; 213 | bool tx_empty = m_tx.size() == 0; 214 | bool rx_empty = m_rx.size() == 0; 215 | bool rx_full = m_rx.size() == SPI_FIFO_DEPTH; 216 | 217 | m_reg[address/4] = (tx_full << SPI_SR_TX_FULL_SHIFT) | 218 | (tx_empty << SPI_SR_TX_EMPTY_SHIFT) | 219 | (rx_empty << SPI_SR_RX_EMPTY_SHIFT) | 220 | (rx_full << SPI_SR_RX_FULL_SHIFT); 221 | } 222 | break; 223 | } 224 | dprintf("SPI: Read %08x=%08x\n", address, m_reg[address/4]); 225 | data = m_reg[address/4]; 226 | return true; 227 | } 228 | 229 | int clock(void) 230 | { 231 | bool enable = (m_reg[SPI_CR/4] & (1 << SPI_CR_SPE_SHIFT)) != 0; 232 | bool loopback = (m_reg[SPI_CR/4] & (1 << SPI_CR_LOOP_SHIFT)) != 0; 233 | bool trans_inhibit = (m_reg[SPI_CR/4] & (1 << SPI_CR_TRANS_INHIBIT_SHIFT)) != 0; 234 | bool global_int_en = (m_reg[SPI_DGIER/4] & (1 << SPI_DGIER_GIE_SHIFT)) != 0; 235 | bool tx_empty_irq_en = (m_reg[SPI_IPIER/4] & (1 << SPI_IPIER_TX_EMPTY_SHIFT)) != 0; 236 | bool tx_empty_event = false; 237 | 238 | if (m_spi_delay == 0) 239 | { 240 | if (enable && !trans_inhibit && m_tx.size() != 0) 241 | { 242 | if (loopback) 243 | rx_push(m_tx.front()); 244 | else 245 | rx_push(0xFF); 246 | m_tx.pop(); 247 | 248 | if (m_tx.size() == 0) 249 | tx_empty_event = true; 250 | } 251 | m_spi_delay = SPI_CLOCK_DIV; 252 | } 253 | else if (enable && !trans_inhibit) 254 | m_spi_delay -= 1; 255 | 256 | // Tx empty event, set interrupt 257 | if (tx_empty_event) 258 | m_reg[SPI_IPISR/4] |= (1 << SPI_IPISR_TX_EMPTY_SHIFT); 259 | 260 | if (!m_irq_inhibit && global_int_en && tx_empty_irq_en && (m_reg[SPI_IPISR/4] & (1 << SPI_IPISR_TX_EMPTY_SHIFT))) 261 | { 262 | m_irq_inhibit = true; 263 | raise_interrupt(); 264 | } 265 | 266 | return 0; 267 | } 268 | 269 | private: 270 | uint32_t m_reg[256]; 271 | std::queue m_tx; 272 | std::queue m_rx; 273 | uint32_t m_spi_delay; 274 | bool m_irq_inhibit; 275 | }; 276 | 277 | #endif 278 | -------------------------------------------------------------------------------- /peripherals/device_systick.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_SYSTICK_H__ 9 | #define __DEVICE_SYSTICK_H__ 10 | 11 | #include "device.h" 12 | 13 | //----------------------------------------------------------------- 14 | // Defines 15 | //----------------------------------------------------------------- 16 | #define TIMER_CSR 0x0 17 | #define TIMER_CSR_ENABLE 0 18 | #define TIMER_CSR_ENABLE_SHIFT 0 19 | #define TIMER_CSR_ENABLE_MASK 0x1 20 | 21 | #define TIMER_CSR_INTERRUPT 1 22 | #define TIMER_CSR_INTERRUPT_SHIFT 1 23 | #define TIMER_CSR_INTERRUPT_MASK 0x1 24 | 25 | #define TIMER_RVR 0x4 26 | #define TIMER_RVR_RELOAD_SHIFT 0 27 | #define TIMER_RVR_RELOAD_MASK 0xffffffff 28 | 29 | #define TIMER_CVR 0x8 30 | #define TIMER_CVR_CURRENT_SHIFT 0 31 | #define TIMER_CVR_CURRENT_MASK 0xffffffff 32 | 33 | //----------------------------------------------------------------- 34 | // TimerSystick: Model of Systick Timer IP 35 | //----------------------------------------------------------------- 36 | class device_systick: public device 37 | { 38 | public: 39 | device_systick(uint32_t base_addr, device *irq_ctrl, int irq_num): device("systick", base_addr, 32, irq_ctrl, irq_num) 40 | { 41 | reset(); 42 | } 43 | 44 | void reset(void) 45 | { 46 | m_irq = false; 47 | m_reg_csr = 0; 48 | m_reg_reload = 0; 49 | m_reg_current = 0; 50 | } 51 | 52 | bool write32(uint32_t address, uint32_t data) 53 | { 54 | address -= m_base; 55 | switch (address) 56 | { 57 | case TIMER_CSR: 58 | m_reg_csr = data; 59 | break; 60 | case TIMER_RVR: 61 | m_reg_reload = data; 62 | break; 63 | default: 64 | fprintf(stderr, "TimerSystick: Bad write @ %08x\n", address); 65 | return false; 66 | } 67 | return true; 68 | } 69 | bool read32(uint32_t address, uint32_t &data) 70 | { 71 | data = 0; 72 | address -= m_base; 73 | 74 | switch (address) 75 | { 76 | case TIMER_CSR: 77 | data = m_reg_csr; 78 | break; 79 | case TIMER_RVR: 80 | data = m_reg_reload; 81 | break; 82 | case TIMER_CVR: 83 | data = m_reg_current; 84 | break; 85 | default: 86 | fprintf(stderr, "TimerSystick: Bad read @ %08x\n", address); 87 | return false; 88 | } 89 | return true; 90 | } 91 | 92 | int clock(void) 93 | { 94 | // Timer disabled 95 | if (!(m_reg_csr & (1 << TIMER_CSR_ENABLE_SHIFT))) 96 | { 97 | m_reg_current = m_reg_reload; 98 | m_irq = false; 99 | } 100 | // Timer expired 101 | else if (m_reg_current == 0) 102 | { 103 | m_reg_current = m_reg_reload; 104 | m_irq = true; 105 | } 106 | else 107 | m_reg_current-= 1; 108 | 109 | // Interrupts enabled 110 | if (m_irq && (m_reg_csr & (1 << TIMER_CSR_INTERRUPT_SHIFT))) 111 | { 112 | raise_interrupt(); 113 | m_irq = false; 114 | } 115 | return 0; 116 | } 117 | 118 | private: 119 | uint32_t m_base_addr; 120 | bool m_irq; 121 | uint32_t m_reg_csr; 122 | uint32_t m_reg_reload; 123 | uint32_t m_reg_current; 124 | }; 125 | 126 | #endif 127 | -------------------------------------------------------------------------------- /peripherals/device_sysuart.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_SYSUART_H__ 9 | #define __DEVICE_SYSUART_H__ 10 | 11 | #include "device.h" 12 | 13 | //----------------------------------------------------------------- 14 | // Model of ARM System UART 15 | //----------------------------------------------------------------- 16 | class device_sysuart: public device 17 | { 18 | public: 19 | device_sysuart(uint32_t base_addr, uint32_t size, device *irq_ctrl, int irq_num): device("sysuart", base_addr, size, irq_ctrl, irq_num) 20 | { 21 | reset(); 22 | } 23 | 24 | void reset(void) 25 | { 26 | 27 | } 28 | 29 | bool write32(uint32_t address, uint32_t data) 30 | { 31 | printf("%c",(data >> 0) & 0xFF); 32 | fflush(stdout); 33 | return true; 34 | } 35 | bool read32(uint32_t address, uint32_t &data) 36 | { 37 | data = 0; 38 | return true; 39 | } 40 | 41 | int clock(void) 42 | { 43 | return 0; 44 | } 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /peripherals/device_timer_clint.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_TIMER_CLINT_H__ 9 | #define __DEVICE_TIMER_CLINT_H__ 10 | 11 | #include "device.h" 12 | #include "cpu.h" 13 | 14 | //----------------------------------------------------------------- 15 | // Defines 16 | //----------------------------------------------------------------- 17 | #define CLINT_REG_MSIP 0x0000 18 | #define CLINT_REG_TIMER_CMP_LO 0x4000 19 | #define CLINT_REG_TIMER_CMP_HI 0x4004 20 | #define CLINT_REG_TIMER_VAL_LO 0xbff8 21 | #define CLINT_REG_TIMER_VAL_HI 0xbffc 22 | #define CLINT_REG_SIZE 0xc000 23 | 24 | #define IRQ_M_TIMER 7 25 | 26 | //----------------------------------------------------------------- 27 | // device_timer_clint: Model of 'Core-Local Interruptor' 28 | //----------------------------------------------------------------- 29 | class device_timer_clint: public device 30 | { 31 | public: 32 | device_timer_clint(uint32_t base_addr, cpu *cpu): device("clint", base_addr, CLINT_REG_SIZE, NULL, 0) 33 | { 34 | m_cpu = cpu; 35 | reset(); 36 | } 37 | 38 | void reset(void) 39 | { 40 | m_reg_cmp = (uint64_t)-1; 41 | m_reg_val = 0; 42 | } 43 | 44 | bool write32(uint32_t address, uint32_t data) 45 | { 46 | address -= m_base; 47 | switch (address) 48 | { 49 | case CLINT_REG_MSIP: 50 | if (data != 0) 51 | { 52 | fprintf(stderr, "CLINT: Error - trying to raise MSIP - not supported yet\n"); 53 | return false; 54 | } 55 | break; 56 | case CLINT_REG_TIMER_CMP_LO: 57 | m_reg_cmp &= ~0xffffffffull; 58 | m_reg_cmp |= data; 59 | break; 60 | case CLINT_REG_TIMER_CMP_HI: 61 | m_reg_cmp &= ~0xffffffff00000000ull; 62 | m_reg_cmp |= ((uint64_t)data) << 32; 63 | break; 64 | default: 65 | fprintf(stderr, "CLINT: Bad write @ %08x\n", address); 66 | return false; 67 | break; 68 | } 69 | return true; 70 | } 71 | bool read32(uint32_t address, uint32_t &data) 72 | { 73 | data = 0; 74 | address -= m_base; 75 | 76 | switch (address) 77 | { 78 | case CLINT_REG_TIMER_CMP_LO: 79 | data = m_reg_cmp >> 0; 80 | break; 81 | case CLINT_REG_TIMER_CMP_HI: 82 | data = m_reg_cmp >> 32; 83 | break; 84 | case CLINT_REG_TIMER_VAL_LO: 85 | data = m_reg_val >> 0; 86 | break; 87 | case CLINT_REG_TIMER_VAL_HI: 88 | data = m_reg_val >> 32; 89 | break; 90 | default: 91 | fprintf(stderr, "CLINT: Bad read @ %08x\n", address); 92 | return false; 93 | break; 94 | } 95 | return true; 96 | } 97 | 98 | int clock(void) 99 | { 100 | m_reg_val += 1; 101 | 102 | // Timer match - should set MIP_MTIP 103 | if (m_reg_val >= m_reg_cmp) 104 | m_cpu->set_interrupt(IRQ_M_TIMER); 105 | else 106 | m_cpu->clr_interrupt(IRQ_M_TIMER); 107 | 108 | return 0; 109 | } 110 | 111 | private: 112 | cpu *m_cpu; 113 | uint64_t m_reg_cmp; 114 | uint64_t m_reg_val; 115 | }; 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /peripherals/device_timer_owl.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_TIMER_OWL_H__ 9 | #define __DEVICE_TIMER_OWL_H__ 10 | 11 | #include "device.h" 12 | 13 | //----------------------------------------------------------------- 14 | // Defines 15 | //----------------------------------------------------------------- 16 | #define TIMER_CTRL 0x0 17 | #define TIMER_CTRL_PD_SHIFT 0 18 | #define TIMER_CTRL_PD_MASK 0x1 19 | #define TIMER_CTRL_INTEN_SHIFT 1 20 | #define TIMER_CTRL_INTEN_MASK 0x1 21 | #define TIMER_CTRL_EN_SHIFT 2 22 | #define TIMER_CTRL_EN_MASK 0x1 23 | 24 | #define TIMER_CMP 0x4 25 | #define TIMER_CMP_VALUE_SHIFT 0 26 | #define TIMER_CMP_VALUE_MASK 0xffffffff 27 | 28 | #define TIMER_VAL 0x8 29 | #define TIMER_VAL_CURRENT_SHIFT 0 30 | #define TIMER_VAL_CURRENT_MASK 0xffffffff 31 | 32 | #define NUM_TIMERS 2 33 | 34 | //----------------------------------------------------------------- 35 | // device_timer_owl: Model of Owl Timer IP 36 | //----------------------------------------------------------------- 37 | class device_timer_owl: public device 38 | { 39 | public: 40 | device_timer_owl(uint32_t base_addr, device *irq_ctrl, int irq_num): device("owl_timer", base_addr, 256, irq_ctrl, irq_num) 41 | { 42 | reset(); 43 | } 44 | 45 | void reset(void) 46 | { 47 | for (int i=0;iputchar(data); 94 | m_reg[UART8250_LSR_OFFSET] = UART8250_LSR_TEMT | UART8250_LSR_THRE; 95 | break; 96 | } 97 | 98 | return false; 99 | } 100 | 101 | bool read8(uint32_t address, uint8_t &data) 102 | { 103 | address -= m_base; 104 | 105 | if (address == UART8250_RBR_OFFSET) 106 | m_rx = -1; 107 | 108 | if (m_rx != -1) 109 | m_reg[UART8250_LSR_OFFSET] |= UART8250_LSR_DR; 110 | else 111 | m_reg[UART8250_LSR_OFFSET] &= ~UART8250_LSR_DR; 112 | 113 | data = m_reg[address]; 114 | 115 | return false; 116 | } 117 | 118 | // 8-bit device... 119 | bool write32(uint32_t address, uint32_t data) { return false; } 120 | bool read32(uint32_t address, uint32_t &data) { return false; } 121 | 122 | int clock(void) 123 | { 124 | // No rx char in the buffer, poll again... 125 | if (m_rx == -1) 126 | { 127 | if (m_poll_count++ == 1024) 128 | { 129 | m_poll_count = 0; 130 | m_rx = m_console->getchar(); 131 | m_reg[UART8250_RBR_OFFSET] = (uint8_t)m_rx; 132 | } 133 | } 134 | 135 | return 0; 136 | } 137 | 138 | private: 139 | console_io *m_console; 140 | uint8_t m_reg[UART8250_REG_SIZE]; 141 | int m_rx; 142 | int m_poll_count; 143 | }; 144 | 145 | #endif 146 | -------------------------------------------------------------------------------- /peripherals/device_uart_lite.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __DEVICE_UART_LITE_H__ 9 | #define __DEVICE_UART_LITE_H__ 10 | 11 | #include "device.h" 12 | #include "console_io.h" 13 | 14 | //----------------------------------------------------------------- 15 | // Defines 16 | //----------------------------------------------------------------- 17 | #define ULITE_RX 0x0 18 | #define ULITE_RX_DATA_SHIFT 0 19 | #define ULITE_RX_DATA_MASK 0xff 20 | 21 | #define ULITE_TX 0x4 22 | #define ULITE_TX_DATA_SHIFT 0 23 | #define ULITE_TX_DATA_MASK 0xff 24 | 25 | #define ULITE_STATUS 0x8 26 | #define ULITE_STATUS_IE 4 27 | #define ULITE_STATUS_IE_SHIFT 4 28 | #define ULITE_STATUS_IE_MASK 0x1 29 | 30 | #define ULITE_STATUS_TXFULL 3 31 | #define ULITE_STATUS_TXFULL_SHIFT 3 32 | #define ULITE_STATUS_TXFULL_MASK 0x1 33 | 34 | #define ULITE_STATUS_TXEMPTY 2 35 | #define ULITE_STATUS_TXEMPTY_SHIFT 2 36 | #define ULITE_STATUS_TXEMPTY_MASK 0x1 37 | 38 | #define ULITE_STATUS_RXFULL 1 39 | #define ULITE_STATUS_RXFULL_SHIFT 1 40 | #define ULITE_STATUS_RXFULL_MASK 0x1 41 | 42 | #define ULITE_STATUS_RXVALID 0 43 | #define ULITE_STATUS_RXVALID_SHIFT 0 44 | #define ULITE_STATUS_RXVALID_MASK 0x1 45 | 46 | #define ULITE_CONTROL 0xc 47 | #define ULITE_CONTROL_IE 4 48 | #define ULITE_CONTROL_IE_SHIFT 4 49 | #define ULITE_CONTROL_IE_MASK 0x1 50 | 51 | #define ULITE_CONTROL_RST_RX 1 52 | #define ULITE_CONTROL_RST_RX_SHIFT 1 53 | #define ULITE_CONTROL_RST_RX_MASK 0x1 54 | 55 | #define ULITE_CONTROL_RST_TX 0 56 | #define ULITE_CONTROL_RST_TX_SHIFT 0 57 | #define ULITE_CONTROL_RST_TX_MASK 0x1 58 | 59 | //----------------------------------------------------------------- 60 | // UartLite: Model of Xilinx UART-Lite IP 61 | //----------------------------------------------------------------- 62 | class device_uart_lite: public device 63 | { 64 | public: 65 | device_uart_lite(uint32_t base_addr, device *irq_ctrl, int irq_num, console_io *con_io): device("uart_lite", base_addr, 256, irq_ctrl, irq_num) 66 | { 67 | m_console = con_io; 68 | 69 | assert(m_console != NULL); 70 | 71 | reset(); 72 | } 73 | 74 | void reset(void) 75 | { 76 | m_irq = false; 77 | m_rx = -1; 78 | m_ctrl = 0; 79 | } 80 | 81 | bool write32(uint32_t address, uint32_t data) 82 | { 83 | address -= m_base; 84 | switch (address) 85 | { 86 | case ULITE_TX: 87 | m_console->putchar(data); 88 | 89 | // Transmit -> empty 90 | m_irq = true; 91 | break; 92 | case ULITE_CONTROL: 93 | m_ctrl = data; 94 | break; 95 | default: 96 | fprintf(stderr, "UARTLITE: Bad write @ %08x\n", address); 97 | exit (-1); 98 | break; 99 | } 100 | return true; 101 | } 102 | bool read32(uint32_t address, uint32_t &data) 103 | { 104 | data = 0; 105 | address -= m_base; 106 | 107 | // Poll for new character 108 | if (m_rx == -1) 109 | { 110 | m_rx = m_console->getchar(); 111 | if (m_rx != -1) 112 | m_irq = true; 113 | } 114 | 115 | switch (address) 116 | { 117 | case ULITE_RX: 118 | data = ((uint32_t)m_rx) & ULITE_RX_DATA_MASK; 119 | m_rx = -1; 120 | break; 121 | case ULITE_CONTROL: 122 | data = m_ctrl; 123 | break; 124 | case ULITE_STATUS: 125 | data |= ((m_ctrl >> ULITE_CONTROL_IE_SHIFT) & 0x1) << ULITE_STATUS_IE_SHIFT; 126 | data |= 0 << ULITE_STATUS_TXFULL_SHIFT; 127 | data |= 1 << ULITE_STATUS_TXEMPTY_SHIFT; 128 | data |= 0 << ULITE_STATUS_RXFULL_SHIFT; 129 | data |= (m_rx != -1) << ULITE_STATUS_RXVALID_SHIFT; 130 | break; 131 | default: 132 | fprintf(stderr, "UARTLITE: Bad read @ %08x\n", address); 133 | exit (-1); 134 | break; 135 | } 136 | return true; 137 | } 138 | 139 | int clock(void) 140 | { 141 | // No rx char in the buffer, poll again... 142 | if (m_rx == -1) 143 | { 144 | static int poll_count = 0; 145 | 146 | if (poll_count++ == 1024) 147 | { 148 | poll_count = 0; 149 | m_rx = m_console->getchar(); 150 | if (m_rx != -1) 151 | m_irq = true; 152 | } 153 | } 154 | 155 | // Interrupts enabled 156 | if (m_irq && (m_ctrl & (1 << ULITE_CONTROL_IE_SHIFT))) 157 | { 158 | raise_interrupt(); 159 | m_irq = false; 160 | } 161 | 162 | return 0; 163 | } 164 | 165 | private: 166 | bool m_irq; 167 | console_io *m_console; 168 | uint32_t m_ctrl; 169 | int m_rx; 170 | }; 171 | 172 | #endif 173 | -------------------------------------------------------------------------------- /platforms/platform.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __PLATFORM_H__ 9 | #define __PLATFORM_H__ 10 | 11 | #include "cpu.h" 12 | 13 | class platform 14 | { 15 | public: 16 | virtual cpu* get_cpu(void) = 0; 17 | }; 18 | 19 | #endif -------------------------------------------------------------------------------- /platforms/platform_basic.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __PLATFORM_BASIC_H__ 9 | #define __PLATFORM_BASIC_H__ 10 | 11 | #include "platform.h" 12 | #include "platform_cpu.h" 13 | 14 | #include "device_uart_lite.h" 15 | #include "device_spi_lite.h" 16 | #include "device_timer_owl.h" 17 | #include "device_irq_ctrl.h" 18 | #include "device_dummy.h" 19 | 20 | #define CONFIG_IRQCTRL_BASE 0x90000000 21 | #define CONFIG_TIMER_BASE 0x91000000 22 | #define CONFIG_TIMER_IRQ 0 23 | #define CONFIG_UARTLITE_BASE 0x92000000 24 | #define CONFIG_UARTLITE_IRQ 1 25 | #define CONFIG_SPILITE_BASE 0x93000000 26 | #define CONFIG_SPILITE_IRQ 2 27 | 28 | class platform_basic: public platform_cpu 29 | { 30 | public: 31 | platform_basic(const char *misa, uint32_t membase, uint32_t memsize, console_io *con_io = NULL): platform_cpu(misa, false, membase, memsize) 32 | { 33 | cpu * pcpu = get_cpu(); 34 | if (!pcpu) 35 | return ; 36 | 37 | // Create IRQ controller 38 | device *irq_ctrl = new device_irq_ctrl(CONFIG_IRQCTRL_BASE, 11); 39 | pcpu->attach_device(irq_ctrl); 40 | 41 | // Timer 42 | pcpu->attach_device(new device_timer_owl(CONFIG_TIMER_BASE, irq_ctrl, CONFIG_TIMER_IRQ)); 43 | 44 | // Uart 45 | pcpu->attach_device(new device_uart_lite(CONFIG_UARTLITE_BASE, irq_ctrl, CONFIG_UARTLITE_IRQ, con_io)); 46 | 47 | // SPI 48 | pcpu->attach_device(new device_spi_lite(CONFIG_SPILITE_BASE, irq_ctrl, CONFIG_SPILITE_IRQ)); 49 | } 50 | }; 51 | 52 | #endif -------------------------------------------------------------------------------- /platforms/platform_cpu.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __PLATFORM_CPU_H__ 9 | #define __PLATFORM_CPU_H__ 10 | 11 | #include "platform.h" 12 | #include "rv32.h" 13 | #include "rv64.h" 14 | #include "armv6m.h" 15 | #include "mips.h" 16 | 17 | #include "device_systick.h" 18 | #include "device_sysuart.h" 19 | #include "device_dummy.h" 20 | 21 | class platform_cpu: public platform 22 | { 23 | public: 24 | platform_cpu(const char *misa, bool support_s = false, uint32_t membase = 0, uint32_t memsize = 0) 25 | { 26 | m_cpu = NULL; 27 | 28 | if (!strncmp(misa, "RV32", 4) || !strncmp(misa, "rv32", 4)) 29 | { 30 | printf("Platform: Select %s\n", misa); 31 | rv32 * cpu = new rv32(membase, memsize); 32 | 33 | // Simplified CSR handling for now... 34 | cpu->enable_compliant_csr(support_s); 35 | 36 | // Optional instruction sets 37 | cpu->enable_rvm((strchr(misa, 'M') || strchr(misa, 'm'))); 38 | cpu->enable_rvc((strchr(misa, 'C') || strchr(misa, 'c'))); 39 | cpu->enable_rva((strchr(misa, 'A') || strchr(misa, 'a'))); 40 | 41 | m_cpu = cpu; 42 | } 43 | else if (!strncmp(misa, "RV64", 4) || !strncmp(misa, "rv64", 4)) 44 | { 45 | printf("Platform: Select %s\n", misa); 46 | rv64 *cpu = new rv64(membase, memsize); 47 | 48 | // Simplified CSR handling for now... 49 | cpu->enable_compliant_csr(support_s); 50 | 51 | // Optional instruction sets 52 | cpu->enable_rvm((strchr(misa, 'M') || strchr(misa, 'm'))); 53 | cpu->enable_rvc((strchr(misa, 'C') || strchr(misa, 'c'))); 54 | cpu->enable_rva((strchr(misa, 'A') || strchr(misa, 'a'))); 55 | 56 | m_cpu = cpu; 57 | } 58 | else if (!strncmp(misa, "armv6", 5)) 59 | { 60 | if (membase == 0) 61 | membase = 0x20000000; 62 | 63 | printf("Platform: Select ARMv6m\n"); 64 | armv6m *cpu = new armv6m(membase, memsize); 65 | 66 | // Simple UART - writes to 0xE0000000 are output on the console 67 | cpu->attach_device(new device_sysuart(0xE0000000, 0x1000, NULL, -1)); 68 | 69 | // Dummy System Control Block - writes have no effect, reads return 0 70 | cpu->attach_device(new device_dummy(0xE000ED00, 36)); 71 | 72 | m_cpu = cpu; 73 | } 74 | else if (!strncmp(misa, "mips1", 5) || !strncmp(misa, "mips", 4)) 75 | { 76 | printf("Platform: Select %s\n", misa); 77 | mips_i * cpu = new mips_i(membase, memsize); 78 | m_cpu = cpu; 79 | } 80 | } 81 | 82 | virtual cpu* get_cpu(void) { return m_cpu; } 83 | cpu * m_cpu; 84 | }; 85 | 86 | #endif -------------------------------------------------------------------------------- /platforms/platform_device_tree.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __PLATFORM_DEVICE_TREE_H__ 9 | #define __PLATFORM_DEVICE_TREE_H__ 10 | 11 | #include "platform.h" 12 | #include "platform_cpu.h" 13 | 14 | #include "device_tree.h" 15 | 16 | class platform_device_tree: public platform_cpu 17 | { 18 | public: 19 | platform_device_tree(const char *misa, const char *filename, console_io *con_io = NULL): platform_cpu(misa, true, 0, 0) 20 | { 21 | m_filename = std::string(filename); 22 | m_console = con_io; 23 | m_this_cpu = NULL; 24 | m_mem_base = 0; 25 | m_mem_size = 0; 26 | m_initrd_base = 0; 27 | m_initrd_size = 0; 28 | } 29 | 30 | virtual cpu* get_cpu(void) 31 | { 32 | if (!m_this_cpu) 33 | { 34 | m_this_cpu = platform_cpu::get_cpu(); 35 | if (!m_this_cpu) 36 | return NULL; 37 | 38 | device_tree fdt(m_filename.c_str(), m_console); 39 | if (!fdt.load(m_this_cpu)) 40 | fprintf(stderr, "ERROR: Failed to open device tree\n"); 41 | 42 | m_mem_base = fdt.get_mem_base(); 43 | m_mem_size = fdt.get_mem_size(); 44 | 45 | m_initrd_base = fdt.get_initrd_base(); 46 | m_initrd_size = fdt.get_initrd_size(); 47 | } 48 | 49 | return m_this_cpu; 50 | } 51 | 52 | // This API only makes sense for systems with a single linear memory 53 | uint32_t get_mem_base(void) { return m_mem_base; } 54 | uint32_t get_mem_size(void) { return m_mem_size; } 55 | 56 | uint32_t get_initrd_base(void) { return m_initrd_base; } 57 | uint32_t get_initrd_size(void) { return m_initrd_size; } 58 | 59 | cpu * m_this_cpu; 60 | std::string m_filename; 61 | console_io * m_console; 62 | uint32_t m_mem_base; 63 | uint32_t m_mem_size; 64 | uint32_t m_initrd_base; 65 | uint32_t m_initrd_size; 66 | }; 67 | 68 | #endif -------------------------------------------------------------------------------- /platforms/platform_virt.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __PLATFORM_VIRT_H__ 9 | #define __PLATFORM_VIRT_H__ 10 | 11 | #include "platform.h" 12 | #include "platform_cpu.h" 13 | 14 | #include "device_uart_8250.h" 15 | #include "device_timer_clint.h" 16 | #include "device_irq_plic.h" 17 | 18 | // OpenSBI peripherals 19 | #define CONFIG_PLIC_BASE 0x0c000000 20 | #define CONFIG_CLINT_BASE 0x02000000 21 | #define CONFIG_UART8250_BASE 0x10000000 22 | 23 | class platform_virt: public platform_cpu 24 | { 25 | public: 26 | platform_virt(const char *misa, uint32_t membase, uint32_t memsize, console_io *con_io = NULL): platform_cpu(misa, true, membase, memsize) 27 | { 28 | if (!m_cpu) 29 | return; 30 | 31 | // OpenSBI peripherals 32 | m_cpu->attach_device(new device_irq_plic(CONFIG_PLIC_BASE, 11)); 33 | m_cpu->attach_device(new device_timer_clint(CONFIG_CLINT_BASE, m_cpu)); 34 | m_cpu->attach_device(new device_uart_8250(CONFIG_UART8250_BASE, NULL, -1, con_io)); 35 | } 36 | }; 37 | 38 | #endif -------------------------------------------------------------------------------- /sbi/sbi.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "sbi.h" 14 | #include "rv32.h" 15 | #include "rv64.h" 16 | #include "bin_load.h" 17 | 18 | //----------------------------------------------------------------- 19 | // SYSCALLs 20 | //----------------------------------------------------------------- 21 | #define SBI_SET_TIMER 0 22 | #define SBI_CONSOLE_PUTCHAR 1 23 | #define SBI_CONSOLE_GETCHAR 2 24 | #define SBI_CLEAR_IPI 3 25 | #define SBI_SEND_IPI 4 26 | #define SBI_REMOTE_FENCE_I 5 27 | #define SBI_REMOTE_SFENCE_VMA 6 28 | #define SBI_REMOTE_SFENCE_VMA_ASID 7 29 | #define SBI_SHUTDOWN 8 30 | #define SBI_EXT_BASE 16 31 | 32 | #define SBI_EXT_SPEC_VERSION 0 33 | #define SBI_EXT_IMPL_ID 1 34 | #define SBI_EXT_IMPL_VERSION 2 35 | #define SBI_EXT_PROBE_EXTENSION 3 36 | #define SBI_EXT_GET_MVENDORID 4 37 | #define SBI_EXT_GET_MARCHID 5 38 | #define SBI_EXT_GET_MIMPID 6 39 | 40 | //----------------------------------------------------------------- 41 | // setup: Setup SBI handler (and load some binaries) 42 | //----------------------------------------------------------------- 43 | bool sbi::setup(cpu *cpu, console_io *conio, uint32_t kernel_addr, uint32_t dtb_addr) 44 | { 45 | if (cpu->get_reg_width() == 64) 46 | { 47 | ((rv64*)cpu)->sbi_boot(kernel_addr, dtb_addr); 48 | ((rv64*)cpu)->enable_mem_unaligned(true); 49 | } 50 | else 51 | { 52 | ((rv32*)cpu)->sbi_boot(kernel_addr, dtb_addr); 53 | ((rv32*)cpu)->enable_mem_unaligned(true); 54 | } 55 | 56 | // Register SBI syscall handler 57 | cpu->set_syscall_handler(new sbi(conio)); 58 | 59 | return true; 60 | } 61 | //----------------------------------------------------------------- 62 | // Construction 63 | //----------------------------------------------------------------- 64 | sbi::sbi(console_io *conio) 65 | { 66 | m_conio = conio; 67 | } 68 | //----------------------------------------------------------------- 69 | // sbi_ext: Extended SBI API 70 | //----------------------------------------------------------------- 71 | uint32_t sbi::sbi_ext(uint32_t fid, uint32_t extid) 72 | { 73 | switch (fid) 74 | { 75 | case SBI_EXT_SPEC_VERSION: 76 | return 0x1; 77 | case SBI_EXT_PROBE_EXTENSION: 78 | switch (extid) 79 | { 80 | case SBI_CONSOLE_PUTCHAR: 81 | case SBI_CONSOLE_GETCHAR: 82 | case SBI_SET_TIMER: 83 | case SBI_EXT_BASE: 84 | return 1; 85 | default: 86 | return 0; 87 | } 88 | break; 89 | default: 90 | return 0; 91 | } 92 | 93 | return 0; 94 | } 95 | //----------------------------------------------------------------- 96 | // syscall_handler: Try and execute a hosted system call 97 | //----------------------------------------------------------------- 98 | bool sbi::syscall_handler(cpu *cpu) 99 | { 100 | int reg_id = 10 + 7; 101 | int reg_a0 = 10; 102 | int reg_ret = 10; 103 | uint64_t a0 = (cpu->get_reg_width() == 64) ? cpu->get_register64(reg_a0 + 0) : cpu->get_register(reg_a0 + 0); 104 | uint64_t a1 = (cpu->get_reg_width() == 64) ? cpu->get_register64(reg_a0 + 1) : cpu->get_register(reg_a0 + 1); 105 | uint64_t a2 = (cpu->get_reg_width() == 64) ? cpu->get_register64(reg_a0 + 2) : cpu->get_register(reg_a0 + 2); 106 | uint64_t which = (cpu->get_reg_width() == 64) ? cpu->get_register64(reg_a0 + 7) : cpu->get_register(reg_a0 + 7); 107 | 108 | #define SET_RET(x) cpu->set_register(reg_ret, (uint32_t)(x)) 109 | 110 | // Only take over syscalls in SUPER mode 111 | if (cpu->get_reg_width() == 64) 112 | { 113 | if (!((rv64*)cpu)->in_super_mode()) 114 | return false; 115 | } 116 | else 117 | { 118 | if (!((rv32*)cpu)->in_super_mode()) 119 | return false; 120 | } 121 | 122 | switch (which) 123 | { 124 | case SBI_SHUTDOWN: 125 | printf("Shutdown...\n"); 126 | exit(0); 127 | return true; 128 | case SBI_CONSOLE_PUTCHAR: 129 | if (m_conio) 130 | m_conio->putchar(a0); 131 | return true; 132 | case SBI_CONSOLE_GETCHAR: 133 | if (m_conio) 134 | { 135 | if (cpu->get_reg_width() == 64) 136 | ((rv64*)cpu)->set_register(reg_ret, (uint64_t)m_conio->getchar()); 137 | else 138 | ((rv32*)cpu)->set_register(reg_ret, (uint32_t)m_conio->getchar()); 139 | } 140 | return true; 141 | case SBI_SET_TIMER: 142 | if (cpu->get_reg_width() == 64) 143 | ((rv64*)cpu)->set_timer(a0); 144 | else 145 | ((rv32*)cpu)->set_timer(a0); 146 | return true; 147 | case SBI_REMOTE_FENCE_I: 148 | case SBI_REMOTE_SFENCE_VMA: 149 | return true; 150 | case SBI_EXT_BASE: 151 | SET_RET(sbi_ext(a0, a1)); 152 | return true; 153 | default: 154 | printf("SBI: Unhandled SYSCALL, stopping... (id=%d)\n", which); 155 | exit(-1); 156 | break; 157 | } 158 | 159 | // Not handled 160 | printf("ECALL: Not handled %d\n", which); 161 | return false; 162 | } 163 | -------------------------------------------------------------------------------- /sbi/sbi.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __SBI_H__ 9 | #define __SBI_H__ 10 | 11 | #include 12 | #include 13 | #include 14 | #include "cpu.h" 15 | #include "syscall_if.h" 16 | 17 | //----------------------------------------------------------------- 18 | // sbi: SBI hosting 19 | //-------------------------------------------------------------------- 20 | class sbi: public syscall_if 21 | { 22 | public: 23 | sbi(console_io *conio); 24 | bool syscall_handler(cpu *instance); 25 | uint32_t sbi_ext(uint32_t fid, uint32_t extid); 26 | 27 | static bool setup(cpu *cpu, console_io *conio, uint32_t kernel_addr, uint32_t dtb_addr); 28 | 29 | protected: 30 | console_io *m_conio; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /virtio/virtio.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __VIRTIO_H__ 9 | #define __VIRTIO_H__ 10 | 11 | #include "device.h" 12 | 13 | //----------------------------------------------------------------- 14 | // Defines 15 | //----------------------------------------------------------------- 16 | #define VIRTIO_QUEUES 8 17 | #define VIRTIO_Q_SIZE 16 18 | 19 | #define VIRTIO_CONFIG_S_ACKNOWLEDGE 1 20 | #define VIRTIO_CONFIG_S_DRIVER 2 21 | #define VIRTIO_CONFIG_S_DRIVER_OK 4 22 | #define VIRTIO_CONFIG_S_FAILED 0x80 23 | 24 | //----------------------------------------------------------------- 25 | // Structures 26 | //----------------------------------------------------------------- 27 | typedef struct 28 | { 29 | /* Address (guest-physical). */ 30 | uint64_t addr; 31 | /* Length. */ 32 | uint32_t len; 33 | 34 | /* This marks a buffer as continuing via the next field. */ 35 | #define VIRTQ_DESC_F_NEXT 1 36 | /* This marks a buffer as device write-only (otherwise device read-only). */ 37 | #define VIRTQ_DESC_F_WRITE 2 38 | /* This means the buffer contains a list of buffer descriptors. */ 39 | #define VIRTQ_DESC_F_INDIRECT 4 40 | /* The flags as indicated above. */ 41 | uint16_t flags; 42 | /* Next field if flags & NEXT */ 43 | uint16_t next; 44 | } t_virtio_desc; 45 | 46 | typedef struct 47 | { 48 | uint32_t ready; 49 | uint32_t num; 50 | uint16_t last_avail_idx; 51 | uint64_t desc_addr; 52 | uint64_t avail_addr; 53 | uint64_t used_addr; 54 | uint32_t notify; 55 | 56 | // Desc 57 | t_virtio_desc desc[VIRTIO_Q_SIZE]; 58 | } t_virtio_q; 59 | 60 | class cpu; 61 | 62 | //----------------------------------------------------------------- 63 | // virtio_device: Interface class 64 | //----------------------------------------------------------------- 65 | class virtio_device 66 | { 67 | public: 68 | virtual int clock(void) { return 0; } 69 | }; 70 | 71 | //----------------------------------------------------------------- 72 | // virtio: MMIO virtio device 73 | //----------------------------------------------------------------- 74 | class virtio: public device 75 | { 76 | public: 77 | virtio(cpu *pcpu, uint32_t base_addr, device *irq_ctrl, int irq_num): device("virtio", base_addr, 4096, irq_ctrl, irq_num) 78 | { 79 | m_mem = pcpu; 80 | m_device_id = 0; 81 | m_vendor_id = 0; 82 | m_features = 0; 83 | 84 | memset(m_cfg_space, 0, sizeof(m_cfg_space)); 85 | reset(); 86 | } 87 | 88 | void set_device(virtio_device *dev, uint32_t device_id, uint32_t vendor_id, uint32_t features) 89 | { 90 | m_dev = dev; 91 | m_device_id = device_id; 92 | m_vendor_id = vendor_id; 93 | m_features = features; 94 | } 95 | 96 | void reset(void); 97 | int clock(void); 98 | virtual int min_access_size(void) { return 1; } 99 | 100 | virtual bool write32(uint32_t address, uint32_t data); 101 | virtual bool read32(uint32_t address, uint32_t &data); 102 | 103 | virtual bool write8(uint32_t addr, uint8_t data); 104 | virtual bool read8(uint32_t addr, uint8_t &data); 105 | 106 | t_virtio_desc get_desc(int q, int idx); 107 | void consume_desc(int queue_idx, int desc_idx, int desc_len); 108 | bool get_desc_size(int *pread_size, int *pwrite_size, int queue_idx, int desc_idx); 109 | 110 | bool queue_access(uint8_t *buf, int queue_idx, int desc_idx, int offset, int count, bool to_queue); 111 | 112 | bool copy_to_queue(int queue_idx, int desc_idx, int offset, uint8_t *buf, int count) 113 | { 114 | return queue_access(buf, queue_idx, desc_idx, offset, count, true); 115 | } 116 | bool copy_from_queue(uint8_t *buf, int queue_idx, int desc_idx, int offset, int count) 117 | { 118 | return queue_access(buf, queue_idx, desc_idx, offset, count, false); 119 | } 120 | 121 | uint16_t get_avail_idx(int queue_idx); 122 | uint16_t get_avail_value(int queue_idx, uint16_t avail_idx); 123 | uint16_t get_used_idx(int queue_idx); 124 | void set_used_idx(int queue_idx, uint16_t value); 125 | 126 | public: 127 | uint32_t m_device_id; 128 | uint32_t m_vendor_id; 129 | uint32_t m_features; 130 | 131 | uint32_t m_status; 132 | uint32_t m_sel_q; 133 | uint32_t m_sel_feat; 134 | uint32_t m_int_status; 135 | 136 | t_virtio_q m_queue[VIRTIO_QUEUES]; 137 | 138 | uint32_t m_cfg_space[256/4]; 139 | cpu *m_mem; 140 | 141 | virtio_device * m_dev; 142 | }; 143 | 144 | #endif -------------------------------------------------------------------------------- /virtio/virtio_block.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "cpu.h" 15 | #include "virtio_block.h" 16 | 17 | //-------------------------------------------------------------------- 18 | // Defines: 19 | //-------------------------------------------------------------------- 20 | #define VIRTIO_BLK_T_IN 0 21 | #define VIRTIO_BLK_T_OUT 1 22 | #define VIRTIO_BLK_T_FLUSH 4 23 | #define VIRTIO_BLK_T_FLUSH_OUT 5 24 | 25 | #define VIRTIO_BLK_S_OK 0 26 | #define VIRTIO_BLK_S_IOERR 1 27 | #define VIRTIO_BLK_S_UNSUPP 2 28 | 29 | #define SECTOR_SIZE 512 30 | 31 | //----------------------------------------------------------------- 32 | // Structures 33 | //----------------------------------------------------------------- 34 | typedef struct 35 | { 36 | uint32_t type; 37 | uint32_t ioprio; 38 | uint64_t sector_num; 39 | } t_virtio_block_hdr; 40 | 41 | //-------------------------------------------------------------------- 42 | // Construction: 43 | //-------------------------------------------------------------------- 44 | virtio_block::virtio_block(virtio *virtio) 45 | { 46 | m_fp = NULL; 47 | m_virtio = virtio; 48 | m_clk_div = 0; 49 | } 50 | //-------------------------------------------------------------------- 51 | // open: 52 | //-------------------------------------------------------------------- 53 | bool virtio_block::open(const char *filename) 54 | { 55 | m_fp = fopen(filename, "rb+"); 56 | if (!m_fp) 57 | return false; 58 | 59 | fseek(m_fp, 0, SEEK_END); 60 | int64_t file_size = ftello(m_fp); 61 | fseek(m_fp, 0, SEEK_SET); 62 | 63 | uint64_t num_sectors = (file_size + 511) / 512; 64 | m_virtio->m_cfg_space[0] = num_sectors >> 0; 65 | m_virtio->m_cfg_space[1] = num_sectors >> 32; 66 | 67 | m_virtio->set_device(this, 2, 0xFFFF, 0); 68 | 69 | return true; 70 | } 71 | //-------------------------------------------------------------------- 72 | // read_block: 73 | //-------------------------------------------------------------------- 74 | bool virtio_block::read_block(uint64_t sector_num, uint8_t *buf, int num_sectors) 75 | { 76 | if (!m_fp) 77 | return false; 78 | 79 | fseek(m_fp, sector_num * SECTOR_SIZE, SEEK_SET); 80 | int res = fread(buf, 1, num_sectors * SECTOR_SIZE, m_fp); 81 | return res == (num_sectors * SECTOR_SIZE); 82 | } 83 | //-------------------------------------------------------------------- 84 | // write_block: 85 | //-------------------------------------------------------------------- 86 | bool virtio_block::write_block(uint64_t sector_num, uint8_t *buf, int num_sectors) 87 | { 88 | if (!m_fp) 89 | return false; 90 | 91 | fseek(m_fp, sector_num * SECTOR_SIZE, SEEK_SET); 92 | int res = fwrite(buf, 1, num_sectors * SECTOR_SIZE, m_fp); 93 | return res == (num_sectors * SECTOR_SIZE); 94 | } 95 | //-------------------------------------------------------------------- 96 | // request: 97 | //-------------------------------------------------------------------- 98 | bool virtio_block::request(int queue_idx, int desc_idx, int read_size, int write_size) 99 | { 100 | t_virtio_block_hdr h; 101 | bool ok; 102 | 103 | // Get request header 104 | if (!m_virtio->copy_from_queue((uint8_t*)&h, queue_idx, desc_idx, 0, sizeof(h))) 105 | return false; 106 | 107 | switch(h.type) 108 | { 109 | // Storage read 110 | case VIRTIO_BLK_T_IN: 111 | { 112 | uint8_t *buf = (uint8_t*)malloc(write_size); 113 | 114 | ok = read_block(h.sector_num, buf, (write_size - 1) / SECTOR_SIZE); 115 | 116 | if (!ok) 117 | buf[write_size - 1] = VIRTIO_BLK_S_IOERR; 118 | else 119 | buf[write_size - 1] = VIRTIO_BLK_S_OK; 120 | 121 | m_virtio->copy_to_queue(queue_idx, desc_idx, 0, buf, write_size); 122 | free(buf); 123 | buf = NULL; 124 | 125 | m_virtio->consume_desc(queue_idx, desc_idx, write_size); 126 | } 127 | break; 128 | // Storage write 129 | case VIRTIO_BLK_T_OUT: 130 | { 131 | assert(write_size >= 1); 132 | int len = read_size - sizeof(h); 133 | uint8_t *buf = (uint8_t*)malloc(len); 134 | 135 | m_virtio->copy_from_queue(buf, queue_idx, desc_idx, sizeof(h), len); 136 | 137 | ok = write_block(h.sector_num, buf, len / SECTOR_SIZE); 138 | 139 | free(buf); 140 | buf = NULL; 141 | 142 | uint8_t resp_buf[1]; 143 | if (!ok) 144 | resp_buf[0] = VIRTIO_BLK_S_IOERR; 145 | else 146 | resp_buf[0] = VIRTIO_BLK_S_OK; 147 | m_virtio->copy_to_queue(queue_idx, desc_idx, 0, resp_buf, sizeof(resp_buf)); 148 | m_virtio->consume_desc(queue_idx, desc_idx, 1); 149 | } 150 | break; 151 | default: 152 | break; 153 | } 154 | return true; 155 | } 156 | //-------------------------------------------------------------------- 157 | // clock: 158 | //-------------------------------------------------------------------- 159 | int virtio_block::clock(void) 160 | { 161 | if (m_clk_div++ < 100) 162 | return 0; 163 | m_clk_div = 0; 164 | 165 | int queue_idx = 0; 166 | int read_size, write_size; 167 | 168 | if (m_virtio->m_queue[queue_idx].last_avail_idx != m_virtio->get_avail_idx(queue_idx)) 169 | { 170 | int desc_idx = m_virtio->get_avail_value(queue_idx, m_virtio->m_queue[queue_idx].last_avail_idx); 171 | 172 | if (m_virtio->get_desc_size(&read_size, &write_size, queue_idx, desc_idx)) 173 | request(queue_idx, desc_idx, read_size, write_size); 174 | 175 | m_virtio->m_queue[queue_idx].last_avail_idx++; 176 | } 177 | 178 | return 0; 179 | } 180 | -------------------------------------------------------------------------------- /virtio/virtio_block.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __VIRTIO_BLOCK_H__ 9 | #define __VIRTIO_BLOCK_H__ 10 | 11 | #include "virtio.h" 12 | 13 | //----------------------------------------------------------------- 14 | // virtio_block: Block VirtIO device 15 | //----------------------------------------------------------------- 16 | class virtio_block: public virtio_device 17 | { 18 | public: 19 | virtio_block(virtio *virtio); 20 | 21 | bool open(const char *filename); 22 | 23 | virtual bool read_block(uint64_t sector_num, uint8_t *buf, int num_sectors); 24 | virtual bool write_block(uint64_t sector_num, uint8_t *buf, int num_sectors); 25 | 26 | bool request(int queue_idx, int desc_idx, int read_size, int write_size); 27 | int clock(void); 28 | 29 | protected: 30 | FILE *m_fp; 31 | virtio *m_virtio; 32 | int m_clk_div; 33 | }; 34 | 35 | #endif -------------------------------------------------------------------------------- /virtio/virtio_net.cpp: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #ifdef INCLUDE_NET_DEVICE 15 | #include "cpu.h" 16 | #include "virtio_net.h" 17 | #include "net_tap.h" 18 | 19 | //-------------------------------------------------------------------- 20 | // Defines: 21 | //-------------------------------------------------------------------- 22 | #define VIRTIO_MAX_MTU 1600 23 | 24 | //-------------------------------------------------------------------- 25 | // Structures 26 | //-------------------------------------------------------------------- 27 | typedef struct 28 | { 29 | uint8_t flags; 30 | uint8_t gso_type; 31 | uint16_t hdr_len; 32 | uint16_t gso_size; 33 | uint16_t csum_start; 34 | uint16_t csum_offset; 35 | uint16_t num_buffers; 36 | } t_virtio_net_hdr; 37 | 38 | //-------------------------------------------------------------------- 39 | // Construction: 40 | //-------------------------------------------------------------------- 41 | virtio_net::virtio_net(virtio *virtio) 42 | { 43 | m_net = NULL; 44 | m_virtio = virtio; 45 | m_clk_div = 0; 46 | } 47 | //-------------------------------------------------------------------- 48 | // open: 49 | //-------------------------------------------------------------------- 50 | bool virtio_net::open(const char *tap_device, uint8_t *mac_addr) 51 | { 52 | m_net = new net_tap(tap_device); 53 | 54 | m_virtio->set_device(this, 1, 0xFFFF, mac_addr ? (1 << 5) : 0); 55 | if (mac_addr) 56 | { 57 | uint8_t *p = (uint8_t*)&m_virtio->m_cfg_space[0]; 58 | memcpy(p, mac_addr, 6); 59 | } 60 | 61 | return true; 62 | } 63 | //-------------------------------------------------------------------- 64 | // has_rx_space: Space in the receive queue (0) 65 | //-------------------------------------------------------------------- 66 | bool virtio_net::has_rx_space(void) 67 | { 68 | if (!m_virtio->m_queue[0].ready) 69 | return false; 70 | 71 | return m_virtio->m_queue[0].last_avail_idx != m_virtio->get_avail_idx(0); 72 | } 73 | //-------------------------------------------------------------------- 74 | // clock: 75 | //-------------------------------------------------------------------- 76 | int virtio_net::clock(void) 77 | { 78 | uint8_t packet[VIRTIO_MAX_MTU]; 79 | 80 | if (m_clk_div++ < 100) 81 | return 0; 82 | m_clk_div = 0; 83 | 84 | // Process network receive 85 | if (has_rx_space()) 86 | { 87 | int packet_len = m_net->receive(packet, sizeof(packet)); 88 | if (packet_len > 0) 89 | { 90 | int queue_idx = 0; 91 | int read_size, write_size; 92 | 93 | int desc_idx = m_virtio->get_avail_value(queue_idx, m_virtio->m_queue[queue_idx].last_avail_idx); 94 | if (m_virtio->get_desc_size(&read_size, &write_size, queue_idx, desc_idx)) 95 | { 96 | t_virtio_net_hdr h; 97 | int hdr_size = sizeof(t_virtio_net_hdr); 98 | memset(&h, 0, hdr_size); 99 | 100 | int len = hdr_size + packet_len; 101 | if (len <= write_size) 102 | { 103 | m_virtio->copy_to_queue(queue_idx, desc_idx, 0, (uint8_t*)&h, hdr_size); 104 | m_virtio->copy_to_queue(queue_idx, desc_idx, hdr_size, packet, packet_len); 105 | m_virtio->consume_desc(queue_idx, desc_idx, len); 106 | m_virtio->m_queue[queue_idx].last_avail_idx++; 107 | } 108 | } 109 | } 110 | } 111 | 112 | // Process network transmit 113 | if (m_virtio->m_queue[1].notify) 114 | { 115 | int queue_idx = 1; 116 | int desc_idx, read_size, write_size; 117 | 118 | uint16_t avail_idx = m_virtio->get_avail_idx(queue_idx); 119 | if (m_virtio->m_queue[queue_idx].last_avail_idx != avail_idx) 120 | { 121 | desc_idx = m_virtio->get_avail_value(queue_idx, m_virtio->m_queue[queue_idx].last_avail_idx); 122 | if (m_virtio->get_desc_size(&read_size, &write_size, queue_idx, desc_idx)) 123 | { 124 | if (read_size < VIRTIO_MAX_MTU) 125 | { 126 | m_virtio->copy_from_queue(packet, queue_idx, desc_idx, 0, read_size); 127 | m_net->send(&packet[12], read_size - 12); 128 | } 129 | 130 | m_virtio->consume_desc(queue_idx, desc_idx, 0); 131 | } 132 | m_virtio->m_queue[queue_idx].last_avail_idx++; 133 | m_virtio->m_queue[queue_idx].notify--; 134 | } 135 | 136 | } 137 | 138 | return 0; 139 | } 140 | #endif -------------------------------------------------------------------------------- /virtio/virtio_net.h: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------------------- 2 | // ExactStep IAISS 3 | // V0.5 4 | // github.com/ultraembedded/exactstep 5 | // Copyright 2014-2019 6 | // License: BSD 3-Clause 7 | //----------------------------------------------------------------- 8 | #ifndef __VIRTIO_NET_H__ 9 | #define __VIRTIO_NET_H__ 10 | 11 | #include "virtio.h" 12 | 13 | class net_tap; 14 | 15 | //----------------------------------------------------------------- 16 | // virtio_net: Net VirtIO device 17 | //----------------------------------------------------------------- 18 | class virtio_net: public virtio_device 19 | { 20 | public: 21 | virtio_net(virtio *virtio); 22 | 23 | bool open(const char *tap_device, uint8_t *mac_addr); 24 | 25 | bool has_rx_space(void); 26 | 27 | int clock(void); 28 | 29 | protected: 30 | net_tap *m_net; 31 | virtio *m_virtio; 32 | int m_clk_div; 33 | 34 | }; 35 | 36 | #endif --------------------------------------------------------------------------------