Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add zig example #140

Merged
merged 1 commit into from Jan 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions riscv-zig-blink/.gitignore
@@ -0,0 +1,3 @@
zig-cache/
riscv-zig-blink
riscv-zig-blink.bin
14 changes: 14 additions & 0 deletions riscv-zig-blink/README.md
@@ -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
@@ -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{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will be able to remove this once ziglang/zig#2826 is complete.

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
@@ -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
@@ -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
@@ -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
@@ -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);
};