Skip to content

Commit

Permalink
Merge pull request #140 from daurnimator/zig
Browse files Browse the repository at this point in the history
Add zig example
  • Loading branch information
xobs committed Jan 28, 2020
2 parents 53cffe1 + 6a60a7a commit 53a62f8
Show file tree
Hide file tree
Showing 12 changed files with 710 additions and 0 deletions.
3 changes: 3 additions & 0 deletions riscv-zig-blink/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
zig-cache/
riscv-zig-blink
riscv-zig-blink.bin
14 changes: 14 additions & 0 deletions riscv-zig-blink/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# riscv-zig-blink

Written against zig 0.5.0+518dbd30c

You can obtain the zig compiler via https://ziglang.org/download/
e.g. a linux user might run:
```
curl -L https://ziglang.org/builds/zig-linux-x86_64-0.5.0+518dbd30c.tar.xz | tar -xJf -
alias zig=./zig-linux-x86_64-0.5.0+518dbd30c/zig
```

Run `zig build --help` from this directory for usage and options.

`zig build run -Drelease` will compile the demo and and run it on your connected FOMU.
46 changes: 46 additions & 0 deletions riscv-zig-blink/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const std = @import("std");
const Builder = std.build.Builder;

pub fn build(b: *Builder) void {
b.setPreferredReleaseMode(.ReleaseSmall);
const mode = b.standardReleaseOptions();

const elf = b.addExecutable("riscv-zig-blink", "src/main.zig");
elf.setTheTarget(.{
.Cross = .{
.arch = .riscv32,
.os = .freestanding,
.abi = .none,
.cpu_features = .{
.cpu = &std.Target.riscv.cpu.generic_rv32,
.features = std.Target.Cpu.Feature.Set.empty,
},
},
});
elf.setLinkerScriptPath("ld/linker.ld");
elf.setBuildMode(mode);
// The ELF file contains debug symbols and can be passed to gdb for remote debugging
if (b.option(bool, "emit-elf", "Should an ELF file be emitted in the current directory?") orelse false) {
elf.setOutputDir(".");
}

const binary = b.addSystemCommand(&[_][]const u8{
b.option([]const u8, "objcopy", "objcopy executable to use (defaults to riscv64-unknown-elf-objcopy)") orelse "riscv64-unknown-elf-objcopy",
"-I",
"elf32-littleriscv",
"-O",
"binary",
});
binary.addArtifactArg(elf);
binary.addArg("riscv-zig-blink.bin");
b.default_step.dependOn(&binary.step);

const run_cmd = b.addSystemCommand(&[_][]const u8{
"dfu-util",
"-D",
"riscv-zig-blink.bin",
});
run_cmd.step.dependOn(&binary.step);
const run_step = b.step("run", "Upload and run the app on your FOMU");
run_step.dependOn(&run_cmd.step);
}
60 changes: 60 additions & 0 deletions riscv-zig-blink/ld/linker.ld
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
ENTRY(_start)

__DYNAMIC = 0;

MEMORY {
sram : ORIGIN = 0x10000000, LENGTH = 0x00020000
rom : ORIGIN = 0x20040000, LENGTH = 0x100000 /* 1 MBit */
}

SECTIONS
{
.text :
{
_ftext = .;
*(.text.start)
*(.text .stub .text.* .gnu.linkonce.t.*)
_etext = .;
} > rom

.rodata :
{
. = ALIGN(4);
_frodata = .;
*(.rodata .rodata.* .gnu.linkonce.r.*)
*(.rodata1)
*(.srodata)
. = ALIGN(4);
_erodata = .;
} > rom

.data : AT (ADDR(.rodata) + SIZEOF (.rodata))
{
. = ALIGN(4);
_fdata = .;
*(.data .data.* .gnu.linkonce.d.*)
*(.data1)
*(.ramtext .ramtext.*)
_gp = ALIGN(16);
*(.sdata .sdata.* .gnu.linkonce.s.* .sdata2 .sdata2.*)
_edata = ALIGN(16); /* Make sure _edata is >= _gp. */
} > sram

.bss :
{
. = ALIGN(4);
_fbss = .;
*(.dynsbss)
*(.sbss .sbss.* .gnu.linkonce.sb.*)
*(.scommon)
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} > sram

_end = .;
}

PROVIDE(_fstack = ORIGIN(sram) + LENGTH(sram) - 4);
75 changes: 75 additions & 0 deletions riscv-zig-blink/src/fomu.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const builtin = @import("builtin");
const std = @import("std");

pub const MESSIBLE = @import("./fomu/messible.zig").MESSIBLE;
pub const REBOOT = @import("./fomu/reboot.zig").REBOOT;
pub const RGB = @import("./fomu/rgb.zig").RGB;
pub const TIMER0 = @import("./fomu/timer0.zig").TIMER0;
pub const TOUCH = @import("./fomu/touch.zig").TOUCH;

pub const SYSTEM_CLOCK_FREQUENCY = 12000000;

pub const start = @import("./fomu/start.zig");
// This forces start.zig file to be imported
comptime {
_ = start;
}

/// Panic function that sets LED to red and flashing + prints the panic message over messible
pub fn panic(message: []const u8, stack_trace: ?*builtin.StackTrace) noreturn {
@setCold(true);

// Put LED into non-raw flashing mode
RGB.CTRL.* = .{
.EXE = true,
.CURREN = true,
.RGBLEDEN = true,
.RRAW = false,
.GRAW = false,
.BRAW = false,
};
// Set colour to red
RGB.setColour(255, 0, 0);
// Enable the LED driver, and set 250 Hz mode.
RGB.setControlRegister(.{
.enable = true,
.fr = .@"250",
.quick_stop = false,
.outskew = false,
.output_polarity = .active_high,
.pwm_mode = .linear,
.BRMSBEXT = 0,
});

messibleOutstream.print("PANIC: {}\r\n", .{message}) catch void;

while (true) {
// TODO: Use @breakpoint() once https://reviews.llvm.org/D69390 is available
asm volatile ("ebreak");
}
}

const OutStream = std.io.OutStream(error{});
pub const messibleOutstream = &OutStream{
.writeFn = struct {
pub fn messibleWrite(self: *const OutStream, bytes: []const u8) error{}!void {
var left: []const u8 = bytes;
while (left.len > 0) {
const bytes_written = MESSIBLE.write(left);
left = left[bytes_written..];
}
}
}.messibleWrite,
};

const InStream = std.io.InStream(error{});
pub const messibleInstream = &InStream{
.writeFn = struct {
pub fn messibleRead(self: *const InStream, buffer: []u8) error{}!usize {
while (true) {
const bytes_read = MESSIBLE.read(buffer);
if (bytes_read != 0) return bytes_read;
}
}
}.messibleRead,
};
52 changes: 52 additions & 0 deletions riscv-zig-blink/src/fomu/messible.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/// Messible: An Ansible for Messages
/// https://rm.fomu.im/messible.html
///
/// An Ansible is a system for instant communication across vast distances,
/// from a small portable device to a huge terminal far away. A Messible is
/// a message- passing system from embedded devices to a host system. You can
/// use it to get very simple printf()-style support over a debug channel.
///
/// The Messible assumes the host has a way to peek into the device’s memory
/// space. This is the case with the Wishbone bridge, which allows both the
/// device and the host to access the same memory.
///
/// At its core, a Messible is a FIFO. As long as the STATUS.FULL bit is 0,
/// the device can write data into the Messible by writing into the IN.
/// However, if this value is 1, you need to decide if you want to wait for it
/// to empty (if the other side is just slow), or if you want to drop the
/// message. From the host side, you need to read STATUS.HAVE to see if there
/// is data in the FIFO. If there is, read OUT to get the most recent byte,
/// which automatically advances the READ pointer.
pub const MESSIBLE = struct {
const base = 0xe0008000;

/// Write half of the FIFO to send data out the Messible. Writing to this register advances the write pointer automatically.
pub const IN = @intToPtr(*volatile u8, base + 0x0);

/// Read half of the FIFO to receive data on the Messible. Reading from this register advances the read pointer automatically.
pub const OUT = @intToPtr(*volatile u8, base + 0x4);

pub const STATUS = @intToPtr(*volatile packed struct {
/// if more data can fit into the IN FIFO.
FULL: bool,

/// if data can be read from the OUT FIFO.
HAVE: bool,
}, base + 0x8);

pub fn write(data: []const u8) usize {
for (data) |c, i| {
if (STATUS.*.FULL) return i;
IN.* = c;
}
return data.len;
}

pub fn read(dst: []u8) usize {
for (dst) |*c, i| {
if (!STATUS.*.HAVE) return i;
c.* = OUT.*;
}
return dst.len;
}
};
22 changes: 22 additions & 0 deletions riscv-zig-blink/src/fomu/reboot.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/// https://rm.fomu.im/reboot.html
pub const REBOOT = struct {
const base = 0xe0006000;

/// Provides support for rebooting the FPGA.
/// You can select which of the four images to reboot to.
pub const CTRL = @intToPtr(*volatile packed struct {
/// Which image to reboot to. SB_WARMBOOT supports four images that are configured at FPGA startup.
/// The bootloader is image 0, so set these bits to 0 to reboot back into the bootloader.
image: u2,

/// A reboot key used to prevent accidental reboots when writing to random areas of memory.
/// To initiate a reboot, set this to 0b101011.
key: u6,
}, base + 0x0);

/// This sets the reset vector for the VexRiscv.
/// This address will be used whenever the CPU is reset, for example
/// through a debug bridge. You should update this address whenever
/// you load a new program, to enable the debugger to run `mon reset`
pub const ADDR = @intToPtr(*volatile u32, base + 0x4);
};

0 comments on commit 53a62f8

Please sign in to comment.