Skip to content

Commit

Permalink
Implement gpio() syscall and a user program for it
Browse files Browse the repository at this point in the history
gpio program allows enabling GPIO pins for output and writing values to
them from the shell.
  • Loading branch information
rtfb committed Aug 31, 2023
1 parent dd09241 commit 7ef0a83
Show file tree
Hide file tree
Showing 13 changed files with 303 additions and 1 deletion.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ USER_DEPS = src/boot.s src/uart-print.s src/baremetal-poweroff.s src/kernel.c \
src/proc_test.c src/spinlock.c src/proc.c src/context.s \
src/pagealloc.c src/fs.c src/bakedinfs.c src/runflags.c \
src/pipe.c src/drivers/drivers.c src/drivers/uart/uart.c \
src/plic.c \
src/plic.c src/gpio.c \
user/src/userland.c user/src/usyscalls.S user/src/user-printf.s \
user/src/user-printf.c user/src/shell.c user/src/ustr.c
TEST_SIFIVE_U_DEPS = $(TEST_DEPS)
Expand Down
84 changes: 84 additions & 0 deletions include/gpio.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#ifndef _GPIO_H_
#define _GPIO_H_

// This is a mapping of on-board Arduino-compatible PIN numbers to the GPIO
// pins internal to the FE310 chip, based on HiFive1 RevB Schematics[1]. In
// GPIO_PIN_x, the x corresponds to the label next to a pin on the board, and
// the value corresponds to a GPIO number as described in FE310-G002 Manual[2],
// Capter 17. For example GPIO_PIN_0 (the corner pin on the board) is connected
// to GPIO number 16.
//
// Note that GPIO_PIN_14 is not connected at all.
//
// [1] https://sifive.cdn.prismic.io/sifive/c34f4c7f-0d3a-493e-8a19-a0b18f8a4555_hifive1-b01-schematics.pdf
// [2] https://sifive.cdn.prismic.io/sifive/034760b5-ac6a-4b1c-911c-f4148bb2c4a5_fe310-g002-v1p5.pdf
//
// TODO: isolate this mapping into machine-specific code.
#define GPIO_PIN_0 16
#define GPIO_PIN_1 17
#define GPIO_PIN_2 18
#define GPIO_PIN_3 19
#define GPIO_PIN_4 20
#define GPIO_PIN_5 21
#define GPIO_PIN_6 22
#define GPIO_PIN_7 23
#define GPIO_PIN_8 0
#define GPIO_PIN_9 1
#define GPIO_PIN_10 2
#define GPIO_PIN_11 3
#define GPIO_PIN_12 4 // XXX: doesn't work
#define GPIO_PIN_13 5
#undef GPIO_PIN_14 // not connected. Make sure it won't compile if we ever use it by mistake
#define GPIO_PIN_15 9
#define GPIO_PIN_16 10 // XXX: doesn't work
#define GPIO_PIN_17 11
#define GPIO_PIN_18 12
#define GPIO_PIN_19 13

#ifndef GPIO_BASE
#define GPIO_BASE 0x10012000
#endif

#define GPIO_PIN_INPUT_VAL 0x00
#define GPIO_PIN_INPUT_EN 0x04
#define GPIO_PIN_OUT_EN 0x08
#define GPIO_PIN_OUT_VAL 0x0c
#define GPIO_PIN_PUE 0x10
#define GPIO_PIN_DS 0x14
// TODO: the rest...
#define GPIO_PIN_IOF_EN 0x38
#define GPIO_PIN_IOF_SEL 0x3c
#define GPIO_PIN_OUT_XOR 0x40
// TODO: the rest...

#define GPIO_ERR_0_1 -1 // can't use GPIO_PIN_0 and GPIO_PIN_1 because they interfere with UART
#define GPIO_ERR_12 -2 // GPIO_PIN_12 does not work yet
#define GPIO_ERR_16 -3 // GPIO_PIN_16 does not work yet
#define GPIO_ERR_BAD_VALUE -4 // unrecognized value passed to gpio_do_syscall
#define GPIO_ERR_BAD_ENABLE -5 // unrecognized enable_disable passed to gpio_do_syscall

// gpio_enable_pin_out enables a specified pin for output.
void gpio_enable_pin_out(int pin);

// gpio_disable_pin_out disables a specified pin for output.
void gpio_disable_pin_out(int pin);

// gpio_set_pin sets a specified pin bit to (bool)value.
void gpio_set_pin(int pin, int value);

// gpio_toggle_pin sets a specified pin value to an opposite of its current value.
void gpio_toggle_pin(int pin);

// gpio_do_syscall implements the system call that interacts with a given GPIO pin.
// enable_disable can be:
// 1 to write-enable pin_num
// 0 to write-disable pin_num
// -1 to leave the pin state as is
//
// value is ignored unless enable_disable is -1. Valid values:
// 0 to write 0 to pin_num
// 1 to write 1 to pin_num
// 2 to toggle pin_num to an opposite value
uint32_t gpio_do_syscall(uint32_t pin_num, uint32_t enable_disable, uint32_t value);

#endif // ifndef _GPIO_H_
1 change: 1 addition & 0 deletions include/syscallnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@
#define SYS_NR_pinfo 33
#define SYS_NR_pgalloc 34
#define SYS_NR_pgfree 35
#define SYS_NR_gpio 36
1 change: 1 addition & 0 deletions include/syscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ uint32_t sys_plist();
uint32_t sys_pinfo();
regsize_t sys_pgalloc();
regsize_t sys_pgfree();
uint32_t sys_gpio();

// These are implemented in assembler as of now:
extern void poweroff();
Expand Down
1 change: 1 addition & 0 deletions src/drivers/uart/uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "proc.h"
#include "drivers/uart/uart.h"
#include "plic.h"
#include "gpio.h"

uart_state_t uart0;

Expand Down
74 changes: 74 additions & 0 deletions src/gpio.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#include "sys.h"
#include "gpio.h"

void gpio_enable_pin_out(int pin) {
uint32_t out_en = *(uint32_t*)(GPIO_BASE + GPIO_PIN_OUT_EN);
out_en |= (1 << pin);
*(uint32_t*)(GPIO_BASE + GPIO_PIN_OUT_EN) = out_en;

uint32_t iof_en = *(uint32_t*)(GPIO_BASE + GPIO_PIN_IOF_EN);
iof_en &= ~(1 << pin);
*(uint32_t*)(GPIO_BASE + GPIO_PIN_IOF_EN) = iof_en;
}

void gpio_disable_pin_out(int pin) {
uint32_t out_en = *(uint32_t*)(GPIO_BASE + GPIO_PIN_OUT_EN);
out_en &= ~(1 << pin);
*(uint32_t*)(GPIO_BASE + GPIO_PIN_OUT_EN) = out_en;

uint32_t iof_en = *(uint32_t*)(GPIO_BASE + GPIO_PIN_IOF_EN);
iof_en |= (1 << pin);
*(uint32_t*)(GPIO_BASE + GPIO_PIN_IOF_EN) = iof_en;
}

void gpio_set_pin(int pin, int value) {
uint32_t val_bits = *(uint32_t*)(GPIO_BASE + GPIO_PIN_OUT_VAL);
if (value) {
val_bits |= (1 << pin);
} else {
val_bits &= ~(1 << pin);
}
*(uint32_t*)(GPIO_BASE + GPIO_PIN_OUT_VAL) = val_bits;
}

void gpio_toggle_pin(int pin) {
uint32_t val_bits = *(uint32_t*)(GPIO_BASE + GPIO_PIN_OUT_VAL);
val_bits ^= (1 << pin);
*(uint32_t*)(GPIO_BASE + GPIO_PIN_OUT_VAL) = val_bits;
}

uint32_t gpio_do_syscall(uint32_t pin_num, uint32_t enable_disable, uint32_t value) {
if (pin_num == GPIO_PIN_0 || pin_num == GPIO_PIN_1) {
return GPIO_ERR_0_1;
}
if (pin_num == GPIO_PIN_12) {
return GPIO_ERR_12;
}
if (pin_num == GPIO_PIN_16) {
return GPIO_ERR_16;
}
switch (enable_disable) {
case 0:
gpio_disable_pin_out(pin_num);
break;
case 1:
gpio_enable_pin_out(pin_num);
break;
case -1:
switch (value) {
case 0:
case 1:
gpio_set_pin(pin_num, value);
break;
case 2:
gpio_toggle_pin(pin_num);
break;
default:
return GPIO_ERR_BAD_VALUE;
}
break;
default:
return GPIO_ERR_BAD_ENABLE;
}
return 0;
}
5 changes: 5 additions & 0 deletions src/proc_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ extern int u_main_coma();
extern int u_main_pipe();
extern int u_main_pipe2();
extern int u_main_wc();
extern int u_main_gpio();

user_program_t userland_programs[MAX_USERLAND_PROGS] _rodata = {
(user_program_t){
Expand Down Expand Up @@ -77,6 +78,10 @@ user_program_t userland_programs[MAX_USERLAND_PROGS] _rodata = {
.entry_point = &u_main_wc,
.name = "wc",
},
(user_program_t){
.entry_point = &u_main_gpio,
.name = "gpio",
},
// keep this last, it's a sentinel:
(user_program_t){
.entry_point = 0,
Expand Down
9 changes: 9 additions & 0 deletions src/syscalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "proc.h"
#include "pagealloc.h"
#include "pipe.h"
#include "gpio.h"

// for fun let's pretend syscall table is kinda like 32bit Linux on x86,
// /usr/include/asm/unistd_32.h: __NR_restart_syscall 0, __NR_exit 1, _NR_fork 2, __NR_read 3, __NR_write 4
Expand All @@ -30,6 +31,7 @@ void *syscall_vector[] _text = {
[SYS_NR_pinfo] sys_pinfo,
[SYS_NR_pgalloc] sys_pgalloc,
[SYS_NR_pgfree] sys_pgfree,
[SYS_NR_gpio] sys_gpio,
};

void syscall() {
Expand Down Expand Up @@ -158,3 +160,10 @@ regsize_t sys_pgfree() {
release_page(page);
return 0;
}

uint32_t sys_gpio() {
uint32_t pin_num = (uint32_t)trap_frame.regs[REG_A0];
uint32_t enable = (uint32_t)trap_frame.regs[REG_A1];
uint32_t value = (uint32_t)trap_frame.regs[REG_A2];
return gpio_do_syscall(pin_num, enable, value);
}
1 change: 1 addition & 0 deletions user/inc/ustr.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@

int _userland ustrncmp(char const *a, char const *b, unsigned int num);
int _userland ustrlen(char const *s);
int _userland uatoi(char const *s, int *err);

#endif // ifndef _USTR_H_
1 change: 1 addition & 0 deletions user/inc/usyscalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ extern uint32_t plist(uint32_t *pids, uint32_t size);
extern uint32_t pinfo(uint32_t pid, pinfo_t *pinfo);
extern regsize_t pgalloc();
extern regsize_t pgfree(void *page);
extern uint32_t gpio(uint32_t pin_num, uint32_t enable, uint32_t value);

#endif // ifndef _USYSCALLS_H_
92 changes: 92 additions & 0 deletions user/src/userland.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "string.h"
#include "syscalls.h"
#include "fs.h"
#include "gpio.h"

int _userland u_main_hello1() {
prints("Hello from hellosayer 1\n");
Expand Down Expand Up @@ -302,3 +303,94 @@ int _userland u_main_coma() {
for (;;)
;
}

int _userland map_gpio_pin_num(int num) {
switch (num) {
case 0: return GPIO_PIN_0;
case 1: return GPIO_PIN_1;
case 2: return GPIO_PIN_2;
case 3: return GPIO_PIN_3;
case 4: return GPIO_PIN_4;
case 5: return GPIO_PIN_5;
case 6: return GPIO_PIN_6;
case 7: return GPIO_PIN_7;
case 8: return GPIO_PIN_8;
case 9: return GPIO_PIN_9;
case 10: return GPIO_PIN_10;
case 11: return GPIO_PIN_11;
case 12: return GPIO_PIN_12; // XXX: doesn't work
case 13: return GPIO_PIN_13;
case 14: return -1; // not connected
case 15: return GPIO_PIN_15;
case 16: return GPIO_PIN_16; // XXX: doesn't work
case 17: return GPIO_PIN_17;
case 18: return GPIO_PIN_18;
case 19: return GPIO_PIN_19;
default:
return -1;
};
}

// gpio is for interacting with gpio pins.
// > gpio N e enable pin N for writing
// > gpio N d disable pin N for writing
// > gpio N 1 write 1 to pin N
// > gpio N 0 write 0 to pin N
// > gpio N t toggle the current value of N
char gpio_parse_err_fmt[] _user_rodata = "ERROR: parse pin_num '%s': %d\n";
char gpio_syscall_err_fmt[] _user_rodata = "ERROR: gpio syscall: %d\n";
int _userland u_main_gpio(int argc, char const *argv[]) {
if (argc < 3) {
prints("usage:\n"
"gpio N e enable pin N for writing\n"
"gpio N d disable pin N for writing\n"
"gpio N 1 write 1 to pin N\n"
"gpio N 0 write 0 to pin N\n"
"gpio N t toggle the current value of N\n");
exit(-1);
}
int parse_err = 0;
int pin_num = uatoi(argv[1], &parse_err);
if (parse_err != 0) {
printf(gpio_parse_err_fmt, argv[1], parse_err);
exit(-2);
}
if (argv[2][1] != 0) {
prints("ERROR: arg 2 must be a single character!\n");
exit(-3);
}
pin_num = map_gpio_pin_num(pin_num);
if (pin_num < 0) {
prints("ERROR: pin mapping error\n");
exit(-4);
}
uint32_t err = 0;
switch (argv[2][0]) {
case 'e':
err = gpio(pin_num, 1, 0);
break;
case 'd':
err = gpio(pin_num, 0, 0);
break;
case '0':
err = gpio(pin_num, -1, 0);
break;
case '1':
err = gpio(pin_num, -1, 1);
break;
case 't':
err = gpio(pin_num, -1, 2);
break;
default:
prints("ERROR: unrecognized arg 2: ");
prints(argv[2]);
prints("\n");
exit(-5);
}
if (err != 0) {
printf(gpio_syscall_err_fmt, err);
exit(-6);
}
exit(0);
return 0;
}
28 changes: 28 additions & 0 deletions user/src/ustr.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,31 @@ int _userland ustrlen(char const *s) {
while (*s++) i++;
return i;
}

int _userland uatoi(char const *s, int *err) {
int multiplier = 1;
if (s[0] == '-') {
multiplier = -1;
s++;
}
int n_digits = 0;
while (*s != 0) {
if (*s < '0' || *s > '9') {
*err = 1;
return 0;
}
s++;
n_digits++;
}
s--;
int number = 0;
int mult_10 = 1;
while (n_digits != 0) {
int digit = *s - '0';
number += digit * mult_10;
mult_10 *= 10;
s--;
n_digits--;
}
return number * multiplier;
}
5 changes: 5 additions & 0 deletions user/src/usyscalls.S
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,8 @@ pgalloc:
pgfree:
macro_syscall SYS_NR_pgfree
ret

.globl gpio
gpio:
macro_syscall SYS_NR_gpio
ret

0 comments on commit 7ef0a83

Please sign in to comment.