lcpu/lua: Add Wiremod interface
Another thing checked off the todo list! Now all I really need to do is uploading (maybe get to work on the project system too?) This removes the lua test device since it was just that - a test
This commit is contained in:
parent
d3cee95b14
commit
6eada83eeb
15
ideas.md
15
ideas.md
|
@ -11,15 +11,15 @@ This is basically the working ideas for the LCPU project.
|
||||||
|
|
||||||
## Code upload
|
## Code upload
|
||||||
|
|
||||||
- Upload a raw binary to execute, generated from any user tooling (goes into server data folder)
|
- Upload a raw binary to execute, generated from any user tooling (goes into server `data/lcpu/users/[steamid]/`)
|
||||||
- Yes, this means you can run Linux in GMod. No, I'm not sorry.
|
- Yes, this means you can run Linux in GMod. No, I'm not sorry.
|
||||||
|
|
||||||
## Integrated simple project workflow
|
## Integrated simple project workflow (WIP, not in the addon yet)
|
||||||
|
|
||||||
- Uses official RISC-V GCC toolchain
|
- Uses official RISC-V GCC toolchain
|
||||||
- In a podman container for jailing reasons?
|
- In a podman container for jailing reasons?
|
||||||
|
|
||||||
- Write assembly/C/C++ code using a tiny project system (source for them would go in server data folder ?)
|
- Write assembly/C/C++ code using a tiny project system (source for them would go in server `data/lcpu/users/[steamid]/projects/[project]`)
|
||||||
- At the root of a project, a `project.json` file is expected to exist, with contents like:
|
- At the root of a project, a `project.json` file is expected to exist, with contents like:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
@ -53,8 +53,8 @@ This is basically the working ideas for the LCPU project.
|
||||||
- `BaseCCompileFlags` and `BaseCppCompileFlags` are defaulted to sane values for each language.
|
- `BaseCCompileFlags` and `BaseCppCompileFlags` are defaulted to sane values for each language.
|
||||||
|
|
||||||
- This will be transpiled into a `Makefile` by the addon.
|
- This will be transpiled into a `Makefile` by the addon.
|
||||||
- A standalone tool will be provided and used for transpiling `project.json` to a `Makefile` (and maybe even passed into the container and transpiled there, to reduce the actions on the host to just the podman run?)
|
- A standalone tool will be provided and used for transpiling `project.json` to a `Makefile` (and maybe even built into the container and transpiled there, to reduce the actions on the host to just the podman run?)
|
||||||
- which, when a Build is done in GMod; is then run with `make` in a temporary podman container which only has access to the source code for the project (and nothing else, besides riscv tools).
|
- which, when a Build is done in GMod; is then run with `make` in a temporary podman container which only has access to the source code folder for the project (and nothing else, besides riscv tools which are in the image).
|
||||||
- Command line is probably something like `make CONFIG=${config}`
|
- Command line is probably something like `make CONFIG=${config}`
|
||||||
- the output binary will be stored alongside the source code on the server side, with a name like `${name}-${config}.bin`
|
- the output binary will be stored alongside the source code on the server side, with a name like `${name}-${config}.bin`
|
||||||
- This file can then be selected for loading (without needing to be uploaded from the client).
|
- This file can then be selected for loading (without needing to be uploaded from the client).
|
||||||
|
@ -63,12 +63,13 @@ This is basically the working ideas for the LCPU project.
|
||||||
- There is no conditional compilation in the `project.json` system
|
- There is no conditional compilation in the `project.json` system
|
||||||
- All files in a project are always built by that project.
|
- All files in a project are always built by that project.
|
||||||
|
|
||||||
- No notion of subprojects/build dependencies other than GCC generated dependencies
|
- No notion of subprojects/build dependencies
|
||||||
- This is meant to be simple for easy development in GMod. If you want complex build features you can export the project onto your own computer and use `lcpu_projgen` to generate Makefiles (which you can then maintain)
|
- This is meant to be simple for easy development in GMod. If you want complex build features you can export the project onto your own computer and use `lcpu_projgen` to generate Makefiles (which you can then maintain)
|
||||||
|
|
||||||
- Text editor used to edit project source files
|
- Text editor used to edit project source files
|
||||||
- Use the Wire editor? (we need wiremod anyways, and the text editor is.. OK I suppose.)
|
- Use the Wire editor? (we need wiremod anyways, and the text editor is.. OK I suppose.)
|
||||||
- Or I guess I could try getting Monaco to play nicely with DHTML
|
- Or: https://github.com/Metastruct/gmod-monaco
|
||||||
|
- https://github.com/JustMrPhoenix/Noir/tree/master
|
||||||
|
|
||||||
- Some example projects?
|
- Some example projects?
|
||||||
- A simple bare metal "Hello World"
|
- A simple bare metal "Hello World"
|
||||||
|
|
|
@ -12,11 +12,11 @@ if SERVER then
|
||||||
LCPU = {};
|
LCPU = {};
|
||||||
LCPU.Devices = {};
|
LCPU.Devices = {};
|
||||||
|
|
||||||
|
-- Uncomment this to enable debug logging (useful for troubleshooting bugs)
|
||||||
--LCPUNative.EnableDebug()
|
--LCPUNative.EnableDebug()
|
||||||
|
|
||||||
AddCSLuaFile("entities/gmod_lcpu_cpu.lua")
|
AddCSLuaFile("entities/gmod_lcpu_cpu.lua")
|
||||||
|
|
||||||
-- Serverside devices
|
-- Serverside devices (that don't depend on wiremod being loaded)
|
||||||
include("lcpu/devices/uart.lua")
|
include("lcpu/devices/uart.lua")
|
||||||
include("lcpu/devices/gmlua_test.lua")
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,10 @@ ENT.Author = "Lily <3"
|
||||||
-- no more, this deeply uses native APIs
|
-- no more, this deeply uses native APIs
|
||||||
if CLIENT then return end
|
if CLIENT then return end
|
||||||
|
|
||||||
|
-- Include the devices which require Wiremod here
|
||||||
|
-- (hacky, but /shrug)
|
||||||
|
include("lcpu/devices/wire_interface.lua")
|
||||||
|
|
||||||
-- TODO: serverside convars to control execution rate & cycle count
|
-- TODO: serverside convars to control execution rate & cycle count
|
||||||
|
|
||||||
function ENT:Initialize()
|
function ENT:Initialize()
|
||||||
|
@ -15,13 +19,11 @@ function ENT:Initialize()
|
||||||
|
|
||||||
-- CPU callbacks?
|
-- CPU callbacks?
|
||||||
self.cpu = LCPUNative.CreateCPU(128 * 1024)
|
self.cpu = LCPUNative.CreateCPU(128 * 1024)
|
||||||
|
|
||||||
-- UART & GLua test device
|
|
||||||
self.uart = LCPU.Devices.UART(0x10000000)
|
self.uart = LCPU.Devices.UART(0x10000000)
|
||||||
self.test_device = LCPU.Devices.LuaTest()
|
self.wireInterface = LCPU.Devices.WireInterface(0x11310000, self, 8, 8)
|
||||||
|
|
||||||
self.cpu:AttachDevice(self.uart)
|
self.cpu:AttachDevice(self.uart)
|
||||||
self.cpu:AttachDevice(self.test_device)
|
self.cpu:AttachDevice(self.wireInterface)
|
||||||
|
|
||||||
self:SetOverlayText("hi :)")
|
self:SetOverlayText("hi :)")
|
||||||
end
|
end
|
||||||
|
@ -29,9 +31,13 @@ end
|
||||||
function ENT:Think()
|
function ENT:Think()
|
||||||
-- Avoid running if the cpu is not powered on
|
-- Avoid running if the cpu is not powered on
|
||||||
if not self.cpu:PoweredOn() then return end
|
if not self.cpu:PoweredOn() then return end
|
||||||
|
self.cpu:Cycle()
|
||||||
-- Even though this is gated by tickrate I'm just trying to be nice here
|
-- Even though this is gated by tickrate I'm just trying to be nice here
|
||||||
self:NextThink(CurTime() + 0.1)
|
self:NextThink(CurTime() + 0.1)
|
||||||
self.cpu:Cycle()
|
end
|
||||||
|
|
||||||
|
function ENT:Reset()
|
||||||
|
self.cpu:Reset()
|
||||||
end
|
end
|
||||||
|
|
||||||
function ENT:PowerOn()
|
function ENT:PowerOn()
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
-- Lua test device. This'll probably get removed soon, this is just for testing
|
|
||||||
-- that Lua->C++ interop actually works like it should
|
|
||||||
|
|
||||||
function LCPU.Devices.LuaTest()
|
|
||||||
local test_device = LCPUNative.CreateDevice(0x11300000, 0x8)
|
|
||||||
test_device.register = 0x0
|
|
||||||
|
|
||||||
function test_device:Peek(address)
|
|
||||||
--print(string.format("TestDevice:Peek @ 0x%08x", address))
|
|
||||||
if address == self.Base then return CurTime() end -- it a test!
|
|
||||||
if address == self.Base + 4 then return self.register end
|
|
||||||
|
|
||||||
return 0xffffffff
|
|
||||||
end
|
|
||||||
|
|
||||||
function test_device:Poke(address, value)
|
|
||||||
--print(string.format("TestDevice:Poke @ 0x%08x -> 0x%08x", address, value))
|
|
||||||
if address == self.Base + 4 then
|
|
||||||
--print("LUAREG write")
|
|
||||||
self.register = value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function test_device:Reset()
|
|
||||||
--print("TestDevice:Reset")
|
|
||||||
-- clear the register
|
|
||||||
self.register = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
return test_device
|
|
||||||
end
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
-- Basic Wiremod interface device.
|
||||||
|
|
||||||
|
function LCPU.Devices.WireInterface(base, entity, nrInputs, nrOutputs)
|
||||||
|
local HEADER_SIZE = 8
|
||||||
|
local function MmioSize()
|
||||||
|
return HEADER_SIZE + (nrInputs * 4) + (nrOutputs * 4)
|
||||||
|
end
|
||||||
|
|
||||||
|
local device = LCPUNative.CreateDevice(base, MmioSize())
|
||||||
|
device.data = {
|
||||||
|
entity = entity,
|
||||||
|
nrInputs = nrInputs,
|
||||||
|
nrOutputs = nrOutputs
|
||||||
|
}
|
||||||
|
|
||||||
|
function device:AddressToIndex(address)
|
||||||
|
return ((address - self.Base) / 4) - 1
|
||||||
|
end
|
||||||
|
|
||||||
|
function device:InitWireStuff()
|
||||||
|
local inputNames = {}
|
||||||
|
local outputNames = {}
|
||||||
|
|
||||||
|
for i = 1, self.data.nrInputs do
|
||||||
|
inputNames[i] = string.format("Input%d", i)
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, self.data.nrOutputs do
|
||||||
|
outputNames[i] = string.format("Output%d", i)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- this will also pollute the attached entity but that's fine
|
||||||
|
self.data.wireInputs = WireLib.CreateSpecialInputs(self.data.entity, inputNames, {}, {})
|
||||||
|
self.data.wireOutputs = WireLib.CreateSpecialOutputs(self.data.entity, outputNames, {}, {})
|
||||||
|
end
|
||||||
|
|
||||||
|
function device:Peek(address)
|
||||||
|
if address == self.Base then return self.data.nrInputs end
|
||||||
|
if address == self.Base + 4 then return self.data.nrOutputs end
|
||||||
|
|
||||||
|
local inputIndex = self:AddressToIndex(address)
|
||||||
|
if inputIndex > self.data.nrInputs then
|
||||||
|
-- Invalid input register read or trying to read an output register
|
||||||
|
return 0xffffffff
|
||||||
|
end
|
||||||
|
|
||||||
|
return self.data.wireInputs[string.format("Input%d", inputIndex)].Value
|
||||||
|
end
|
||||||
|
|
||||||
|
function device:Poke(address, value)
|
||||||
|
if address == self.Base or address == self.Base + 4 then return end -- Don't allow writing read-only registers
|
||||||
|
local outputIndex = self:AddressToIndex(address) - self.data.nrInputs
|
||||||
|
if outputIndex > self.data.nrInputs then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
WireLib.TriggerOutput(self.data.entity, string.format("Output%d", outputIndex), value)
|
||||||
|
end
|
||||||
|
|
||||||
|
function device:Reset()
|
||||||
|
for i = 1, self.data.nrOutputs do
|
||||||
|
WireLib.TriggerOutput(self.data.entity, string.format("Output%d", i), 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
device:InitWireStuff()
|
||||||
|
return device
|
||||||
|
end
|
|
@ -49,6 +49,12 @@ if SERVER then
|
||||||
end
|
end
|
||||||
|
|
||||||
function TOOL:LeftClick_Update(trace)
|
function TOOL:LeftClick_Update(trace)
|
||||||
|
-- power on (TODO)
|
||||||
|
if trace.Entity ~= nil then
|
||||||
|
trace.Entity:Reset()
|
||||||
|
trace.Entity:PowerOn()
|
||||||
|
end
|
||||||
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
function TOOL:MakeEnt(ply, model, Ang, trace)
|
function TOOL:MakeEnt(ply, model, Ang, trace)
|
||||||
|
@ -62,7 +68,7 @@ if SERVER then
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
self:LeftClick_Update(trace)
|
--self:LeftClick_Update(trace)
|
||||||
|
|
||||||
return ent
|
return ent
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,7 +25,6 @@ namespace lcpu {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
self->poweredOn = false;
|
self->poweredOn = false;
|
||||||
self->system->bus->Reset();
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +69,6 @@ namespace lcpu {
|
||||||
system = riscv::System::Create(memorySize);
|
system = riscv::System::Create(memorySize);
|
||||||
system->OnPowerOff = [&]() {
|
system->OnPowerOff = [&]() {
|
||||||
poweredOn = false;
|
poweredOn = false;
|
||||||
system->bus->Reset();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// lame test code. this WILL be removed, I just want this for a quick test
|
// lame test code. this WILL be removed, I just want this for a quick test
|
||||||
|
|
|
@ -1,8 +1,28 @@
|
||||||
// a simple test program - this version talks to a device
|
// Test program for lcpu
|
||||||
// that is implemented in GLua thanks to the LCPU native addon
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// MMIO types
|
||||||
|
|
||||||
|
/// This structure is at WIRE_BASE
|
||||||
|
typedef struct {
|
||||||
|
uint32_t nrInputs;
|
||||||
|
uint32_t nrOutputs;
|
||||||
|
} WireDevice_Header;
|
||||||
|
|
||||||
|
#define WIRE_BASE 0x11310000
|
||||||
|
#define WIRE_HEADER ((volatile WireDevice_Header*)WIRE_BASE)
|
||||||
|
#define WIRE_IO_BASE WIRE_BASE + sizeof(WireDevice_Header)
|
||||||
|
#define WIRE_OUTPUT_BASE WIRE_IO_BASE + (WIRE_HEADER->nrInputs * sizeof(uint32_t))
|
||||||
|
#define WIRE_INPUT(i) *(volatile const uint32_t*)(WIRE_IO_BASE + (i * sizeof(uint32_t)))
|
||||||
|
#define WIRE_OUTPUT(i) *(volatile uint32_t*)(WIRE_OUTPUT_BASE + (i * sizeof(uint32_t)))
|
||||||
|
|
||||||
|
#define SYSCON *(volatile uint32_t*)0x11100000
|
||||||
|
|
||||||
|
#define UART_BASE 0x10000000
|
||||||
|
#define UART_DATA *(volatile uint32_t*)UART_BASE
|
||||||
|
#define UART_STATUS UART_DATA
|
||||||
|
|
||||||
uint32_t strlen(const char* str) {
|
uint32_t strlen(const char* str) {
|
||||||
if(!str)
|
if(!str)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -12,15 +32,6 @@ uint32_t strlen(const char* str) {
|
||||||
return c - str;
|
return c - str;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GLUA_DEVICE_BASE 0x11300000 // base address of the lua test device
|
|
||||||
#define GLUA_DEVICE_WORLDTIME *(volatile uint32_t*)GLUA_DEVICE_BASE // world time register (read only)
|
|
||||||
#define GLUA_DEVICE_LUAREG *(volatile uint32_t*)(GLUA_DEVICE_BASE + 4) // lua number register (read/write)
|
|
||||||
|
|
||||||
#define SYSCON *(volatile uint32_t*)0x11100000
|
|
||||||
|
|
||||||
#define UART_BASE 0x10000000
|
|
||||||
#define UART_DATA *(volatile uint32_t*)UART_BASE
|
|
||||||
#define UART_STATUS UART_DATA
|
|
||||||
|
|
||||||
void putc(char c) {
|
void putc(char c) {
|
||||||
UART_DATA = (uint32_t)c;
|
UART_DATA = (uint32_t)c;
|
||||||
|
@ -74,8 +85,6 @@ void vprintf(const char* format, va_list val) {
|
||||||
case '%':
|
case '%':
|
||||||
if(format[i + 1] == '%')
|
if(format[i + 1] == '%')
|
||||||
putc('%');
|
putc('%');
|
||||||
if(format[i+1] == '\0')
|
|
||||||
return;
|
|
||||||
switch(format[i + 1]) {
|
switch(format[i + 1]) {
|
||||||
case 'i':
|
case 'i':
|
||||||
case 'd': {
|
case 'd': {
|
||||||
|
@ -115,16 +124,21 @@ void printf(const char* format, ...) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
puts("fuck you garry I win\n");
|
printf("Wire interface: %d inputs, %d outputs\n", WIRE_HEADER->nrInputs, WIRE_HEADER->nrOutputs);
|
||||||
|
|
||||||
for(int i = 0; i < 8; ++i)
|
for(uint32_t i = 0; i < WIRE_HEADER->nrInputs; ++i)
|
||||||
printf("GLUA_DEVICE_WORLDTIME reading says -> %d\n", GLUA_DEVICE_WORLDTIME);
|
printf("Wire input %d: value %d\n", i, WIRE_INPUT(i));
|
||||||
|
|
||||||
// try writing to it
|
for(uint32_t i = 0; i < WIRE_HEADER->nrOutputs; ++i) {
|
||||||
GLUA_DEVICE_LUAREG = 0x1234;
|
printf("Setting wire output %d\n", i);
|
||||||
|
WIRE_OUTPUT(i) = 10 + i;
|
||||||
for(int i = 0; i < 8; ++i)
|
}
|
||||||
printf("GLUA_DEVICE_LUAREG reading says -> %d\n", GLUA_DEVICE_LUAREG);
|
|
||||||
|
#if 1
|
||||||
SYSCON = 0x5555;
|
for(;;)
|
||||||
|
;
|
||||||
|
__builtin_unreachable();
|
||||||
|
#else
|
||||||
|
SYSCON = 0x5555;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue