Skip to content

Commit

Permalink
Add zig example
Browse files Browse the repository at this point in the history
  • Loading branch information
daurnimator committed Jan 27, 2020
1 parent 53cffe1 commit d3e5050
Show file tree
Hide file tree
Showing 12 changed files with 442 additions and 0 deletions.
2 changes: 2 additions & 0 deletions riscv-zig-blink/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
zig-cache/
riscv-zig-blink.bin
6 changes: 6 additions & 0 deletions riscv-zig-blink/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# riscv-zig-blink

Written against zig 0.5.0-518dbd30c

Run `zig build --help` from this directory for usage and options.
`zig build run -Drelease` will compile and run on your connected FOMU.
42 changes: 42 additions & 0 deletions riscv-zig-blink/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
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);

const binary = b.addSystemCommand(&[_][]const u8{
"llvm-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);
38 changes: 38 additions & 0 deletions riscv-zig-blink/src/fomu.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
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 start = @import("./fomu/start.zig");
// This forces start.zig file to be imported
comptime {
_ = start;
}

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);
};
55 changes: 55 additions & 0 deletions riscv-zig-blink/src/fomu/rgb.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/// https://rm.fomu.im/rgb.html
pub const RGB = struct {
const base = 0xe0006800;

/// This is the value for the SB_LEDDA_IP.DAT register.
/// It is directly written into the SB_LEDDA_IP hardware block, so you
/// should refer to http://www.latticesemi.com/view_document?document_id=50668.
/// The contents of this register are written to the address specified
/// in ADDR immediately upon writing this register.
pub const DAT = @intToPtr(*volatile u8, base + 0x0);

/// This register is directly connected to SB_LEDDA_IP.ADDR.
/// This register controls the address that is updated whenever DAT is written.
/// Writing to this register has no immediate effect – data isn’t written until the DAT register is written.
pub const ADDR = @intToPtr(*volatile u4, base + 0x4);

/// Control logic for the RGB LED and LEDDA hardware PWM LED block.
pub const CTRL = @intToPtr(*volatile packed struct {
/// Enable the fading pattern?
/// Connected to `SB_LEDDA_IP.LEDDEXE`.
EXE: bool,

/// Enable the current source?
/// Connected to `SB_RGBA_DRV.CURREN`.
CURREN: bool,

/// Enable the RGB PWM control logic?
/// Connected to `SB_RGBA_DRV.RGBLEDEN`.
RGBLEDEN: bool,

/// Enable raw control of the red LED via the RAW.R register.
RRAW: bool,

/// Enable raw control of the green LED via the RAW.G register.
GRAW: bool,

/// Enable raw control of the blue LED via the RAW.B register.
BRAW: bool,
}, base + 0x8);

/// Normally the hardware SB_LEDDA_IP block controls the brightness of the
/// LED, creating a gentle fading pattern.
/// However, by setting the appropriate bit in CTRL, it is possible to
/// manually control the three individual LEDs.
pub const RAW = @intToPtr(*volatile packed struct {
/// Red
R: bool,

/// Green
G: bool,

/// Blue
B: bool,
}, base + 0xc);
};
56 changes: 56 additions & 0 deletions riscv-zig-blink/src/fomu/start.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const root = @import("root");
const std = @import("std");

extern var _ftext: u8;
extern var _etext: u8;
extern var _frodata: u8 align(4);
extern var _erodata: u8 align(4);
extern var _fdata: u8 align(4);
extern var _gp: u8 align(16);
extern var _edata: u8 align(16);
extern var _fbss: u8 align(4);
extern var _ebss: u8 align(4);
extern var _end: u8;
extern var _fstack: u8;

export fn _start() linksection(".text.start") callconv(.Naked) noreturn {
asm volatile (
\\ j over_magic
\\ .word 0xb469075a // Magic value for config flags
\\ .word 0x00000020 // USB_NO_RESET flag so we can attach the debugger
\\over_magic:
);

// set the stack pointer to `&_fstack`
asm volatile (
\\ la sp, _fstack + 4
);

// zero out bss
asm volatile (
\\ la a0, _fbss
\\ la a1, _ebss
\\bss_loop:
\\ beq a0, a1, bss_done
\\ sw zero, 0(a0)
\\ add a0, a0,4
\\ j bss_loop
\\bss_done:
);

// copy data from data rom (which is after rodata) to data
asm volatile (
\\ la t0, _erodata
\\ la t1, _fdata
\\ la t2, _edata
\\data_loop:
\\ lw t3, 0(t0)
\\ sw t3, 0(t1)
\\ addi t0, t0, 4
\\ addi t1, t1, 4
\\ bltu t1, t2, data_loop
);

// call user's main
root.main();
}
Loading

0 comments on commit d3e5050

Please sign in to comment.