From 7732c3a7866d89cb252148e62c00dd88a1cc5495 Mon Sep 17 00:00:00 2001 From: Skyus Date: Sat, 12 Aug 2017 17:59:31 +0200 Subject: [PATCH] Swiftlog v0.5 +Made Swiftlog much more flexible +Started on Swift API +Greatly improved simulation shell script --- .gitignore | 3 +- Package.swift => Package.swiftlog | 1 + README.md | 27 ++++-- Swiftlog/Procedure.swift | 92 ++++++++++++++----- Swiftlog/Utils.swift | 9 +- .../Startup.swift | 34 ++----- {Verilog => SwiftlogExample}/main.v | 2 +- {Verilog => SwiftlogExample}/mux.v | 0 VPIAssistant/VPIAssistant.c | 4 +- build.sh | 14 --- run.sh | 1 - simulate | 61 ++++++++++++ 12 files changed, 171 insertions(+), 77 deletions(-) rename Package.swift => Package.swiftlog (75%) rename Swiftlog/Swiftlog.swift => SwiftlogExample/Startup.swift (73%) rename {Verilog => SwiftlogExample}/main.v (94%) rename {Verilog => SwiftlogExample}/mux.v (100%) delete mode 100755 build.sh delete mode 100755 run.sh create mode 100755 simulate diff --git a/.gitignore b/.gitignore index 5b9e62b..34a04b7 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ build/ DerivedData/ Executables/ -Package.pins \ No newline at end of file +Package.pins +Package.swift \ No newline at end of file diff --git a/Package.swift b/Package.swiftlog similarity index 75% rename from Package.swift rename to Package.swiftlog index edc3778..c787247 100644 --- a/Package.swift +++ b/Package.swiftlog @@ -3,6 +3,7 @@ import PackageDescription let package = Package( name: "Swiftlog", targets: [ + Target(name: packageName, dependencies: ["Swiftlog", "VPIAssistant"]), Target(name: "Swiftlog", dependencies: ["VPIAssistant"]), Target(name: "VPIAssistant") ], diff --git a/README.md b/README.md index 308fcf9..8aac78e 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,25 @@ Swiftlog is a Swift bridge for IcarusVerilog's VPI (PLI 2.0). A Swiftier API is in the works, but ~all~ of your logic can be written in Swift! # Usage -To build, run *build.sh*. This compiles the C, Swift and Verilog components, and generates two files inside **Executables**: main.vpi and main.vpp. +Make a folder with your VPI module's name. Copy all verilog modules inside, and make sure all includes use that folder as root (i.e. \`include "Mux.v", not \`include "SwiftlogExample/Mux.v"). -To run, run *run.sh*. If all goes well, you should see: +Create a file called Startup.swift, and use this template: + +```swift +//SWIFTLOG: +import Swiftlog +import VPIAssistant + +@_cdecl("swiftlog_startup") +func startup() +{ + //Initialize here... +} +``` + +Then set up your procedures where it says initialize here. + +An example has been provided, to run it, just write `./simulate SwiftlogExample`. If all goes well, you should see. ```bash Hello from Verilog! @@ -14,13 +30,11 @@ To run, run *run.sh*. If all goes well, you should see: Result: 999 ``` -And you're ready to go! Use all the Swift files you want past this point by putting them in Swiftlog/. On startup, it calls Swiftlog.startup()- so initialize static classes there. - Your clock should be coming from Verilog, and you should provide update functions as such. # Requirements -You need Swift 3.1-dev installed on your device, which you can get from [Swift.org](https://swift.org/download/#swift-31-development), and set it to work with bash. Swift 3.0 might also work, but just hasn't been been tested yet. +You need Swift 3.1, which you can get from [Swift.org](https://swift.org/download), and add it to the PATH. On macOS, that means Xcode and the Xcode commandline tools must be active. ## Ubuntu Other than Swift, you'll just need Clang and IcarusVerilog. @@ -37,8 +51,7 @@ You need the latest version of Xcode and IcarusVerilog headers installed to /usr ``` # To-do -* Swiftier syntax. This is no more than an API import with the necessary modifications to make it link against iverilog at the moment. -* Makefile? +* Expose arguments in a Swiftier, friendlier way: currently you still have to use the C iterators and such to get your arguments. # License GNU General Public License v2 or (at your option), any later version. Check 'LICENSE'. diff --git a/Swiftlog/Procedure.swift b/Swiftlog/Procedure.swift index b58c482..202dad3 100644 --- a/Swiftlog/Procedure.swift +++ b/Swiftlog/Procedure.swift @@ -1,5 +1,7 @@ import VPIAssistant +typealias PLIInt32 = PLI_INT32 + public enum Value: Int { case BinaryString = 1 @@ -77,8 +79,34 @@ public enum ProcedureType: Int case Func = 2 } + + +func compiletf(user_data: UnsafeMutablePointer?) -> PLIInt32 +{ + if let data = user_data, let procedure = Procedure.dictionary[String(cString: data)] + { + return procedure.compile() + } + print("Fatal Error: Failed to get name of last function called.") + Control.finish() + return 0 +} + +func calltf(user_data: UnsafeMutablePointer?) -> PLIInt32 +{ + if let data = user_data, let procedure = Procedure.dictionary[String(cString: data)] + { + return procedure.call() + } + print("Fatal Error: Failed to get name of last function called.") + Control.finish() + return 0 +} + + public class Procedure { + public static var dictionary: [String: Procedure] = [:] private var store: s_vpi_systf_data private var name: String private var cNameSize: Int @@ -89,7 +117,7 @@ public class Procedure private var validate: ((_: inout [Any]) -> (Bool))? private var execute: (_: inout [Any]) -> (Bool) - init(name: String, type: ProcedureType = .Task, arguments: [Object], validationClosure: ((_: inout [Any]) -> (Bool))? = nil, executionClosure: @escaping (_: inout [Any]) -> (Bool), register: Bool = true) + public init(name: String, type: ProcedureType = .Task, arguments: [Object] = [], validationClosure: ((_: inout [Any]) -> (Bool))? = nil, executionClosure: @escaping (_: inout [Any]) -> (Bool), register: Bool = false) { self.name = name self.type = type @@ -102,26 +130,35 @@ public class Procedure self.cNameSize = pointers.elementCount self.cNamePointer = pointers.mutable self.store.tfname = pointers.cLiteral + self.store.user_data = pointers.cMutable - self.store.type = PLI_INT32(self.type.rawValue) - self.store.compiletf = { - (user_data: UnsafeMutablePointer?) -> Int32 in - return 0 - } //So it doesn't complain about not everything being initialized before self is used in a method like a goddamn baby - self.store.calltf = { - (user_data: UnsafeMutablePointer?) -> Int32 in - return 0 - } //See above - self.registered = true - // self.store.compiletf = { - // (user_data: UnsafeMutablePointer?) -> Int32 in - // return self.compile(user_data: user_data) - // } //This is where I hit a brick wall, I need to capture context for this C function, which Swift won't allow me to. Perhaps I'll need to rethink all of this... - vpi_register_systf(&self.store); + self.store.type = PLIInt32(self.type.rawValue) + self.store.compiletf = compiletf + self.store.calltf = calltf + + if (register) + { + vpi_register_systf(&self.store) + self.registered = true + Procedure.dictionary[name] = self + } + else + { + self.registered = false + } + } + public func register() + { + if (!registered) + { + vpi_register_systf(&self.store) + self.registered = true + Procedure.dictionary[name] = self + } } - func compile(user_data: UnsafeMutablePointer?) -> Int32 + func compile() -> PLIInt32 { guard let handle = vpi_handle(vpiSysTfCall, nil) else @@ -131,13 +168,12 @@ public class Procedure return 0 } - if arguments.count >= 0 - { - + if arguments.count > 0 + { guard let iterator = vpi_iterate(vpiArgument, handle) else { - print("$\(self.name) requires \(arguments.count) argument(s). The simulation will abort.") + print("\(self.name) requires \(arguments.count) argument(s). The simulation will abort.") Control.finish() return 0 } @@ -150,7 +186,7 @@ public class Procedure if Int(type) != self.arguments[count].type?.rawValue { - print("$\(self.name), argument \(count): Invalid argument type.") + print("\(self.name), argument \(count): Invalid argument type.") Control.finish() return 0 } @@ -158,14 +194,24 @@ public class Procedure if count != arguments.count { - print("$\(self.name) requires \(arguments.count) argument(s) (\(count) provided). The simulation will abort.") + print("\(self.name) requires \(arguments.count) argument(s) (\(count) provided). The simulation will abort.") Control.finish() } + + //TODO: Also make user-validation available + } return 0 } + func call() -> PLIInt32 + { + //TODO: make arguments more easily accessible + var emptyArray: [Any] = [] + return execute(&emptyArray) ? 0 : -1 + } + deinit { self.cNamePointer.deallocate(capacity: self.cNameSize) diff --git a/Swiftlog/Utils.swift b/Swiftlog/Utils.swift index 9f84b73..c464050 100644 --- a/Swiftlog/Utils.swift +++ b/Swiftlog/Utils.swift @@ -1,9 +1,9 @@ import Foundation import VPIAssistant -class Control +public class Control { - static func finish() + public static func finish() { vpi_finish(); } @@ -11,9 +11,10 @@ class Control extension String { - var cPointer: (cLiteral: UnsafePointer, cMutable: UnsafeMutablePointer, mutable: UnsafeMutablePointer, elementCount: Int) + public var cPointer: (cLiteral: UnsafePointer, cMutable: UnsafeMutablePointer, mutable: UnsafeMutablePointer, elementCount: Int) { - let utf8Representation = Array(self.utf8) + var utf8Representation = Array(self.utf8) + utf8Representation.append(0) //0 terminator let mutablePointer = UnsafeMutablePointer.allocate(capacity: utf8Representation.count) let cMutablePointer = UnsafeMutableRawPointer(mutablePointer).bindMemory(to: CChar.self, capacity: utf8Representation.count) let immutablePointer = UnsafeRawPointer(mutablePointer).bindMemory(to: CChar.self, capacity: utf8Representation.count) diff --git a/Swiftlog/Swiftlog.swift b/SwiftlogExample/Startup.swift similarity index 73% rename from Swiftlog/Swiftlog.swift rename to SwiftlogExample/Startup.swift index f04fb1b..9b84129 100644 --- a/Swiftlog/Swiftlog.swift +++ b/SwiftlogExample/Startup.swift @@ -1,33 +1,19 @@ +//SWIFTLOG: main.v +import Swiftlog import VPIAssistant -/* - startup - - Do ALL your setup here. No, really. ~All of it~. This is the equivalent of a main function. -*/ - -@_cdecl("startup") +@_cdecl("swiftlog_startup") func startup() { - //Register Function "$hello_world" - var helloWorld = s_vpi_systf_data() - helloWorld.type = vpiSysTask - helloWorld.tfname = "$hello_world".cPointer.cLiteral - helloWorld.calltf = { - (user_data: UnsafeMutablePointer?) -> Int32 in - print(user_data?.pointee) + //Swiftlog Style + let helloWorld = Procedure(name: "$hello_world", executionClosure: { + _ in print("...and Hello from Swift!") - return 0 - } - helloWorld.compiletf = { - (user_data: UnsafeMutablePointer?) -> Int32 in - return 0 - } - helloWorld.sizetf = nil - helloWorld.user_data = "scrambld, egge".cPointer.cMutable - vpi_register_systf(&helloWorld); + return true + }) + helloWorld.register() - //Register Function "$hello_world" + //C Style var showResult = s_vpi_systf_data(); showResult.type = vpiSysTask showResult.tfname = "$show_result".cPointer.cLiteral diff --git a/Verilog/main.v b/SwiftlogExample/main.v similarity index 94% rename from Verilog/main.v rename to SwiftlogExample/main.v index 4befc34..877ff95 100644 --- a/Verilog/main.v +++ b/SwiftlogExample/main.v @@ -1,7 +1,7 @@ //main Verilog Module `timescale 1ns/1ns -`include "Verilog/mux.v" +`include "mux.v" module main; diff --git a/Verilog/mux.v b/SwiftlogExample/mux.v similarity index 100% rename from Verilog/mux.v rename to SwiftlogExample/mux.v diff --git a/VPIAssistant/VPIAssistant.c b/VPIAssistant/VPIAssistant.c index 5685d91..a1ca17e 100644 --- a/VPIAssistant/VPIAssistant.c +++ b/VPIAssistant/VPIAssistant.c @@ -5,11 +5,11 @@ /* This is the part of the code that is not exactly preferable, but there isn't really a better way, it's impossible to set vlog_startup_routines anywhere in Swift. */ -extern void startup(void); +extern void swiftlog_startup(void); void (*vlog_startup_routines[])() = { - startup, + swiftlog_startup, 0 }; diff --git a/build.sh b/build.sh deleted file mode 100755 index 5a886fe..0000000 --- a/build.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/bash -unamestr=`uname` - -mkdir -p Executables/ -iverilog -o Executables/main.vvp Verilog/main.v - -# macOS using Mach-style binaries means leaving some symbols hanging like that isn't viable without a special option. -if [ $unamestr = "Darwin" ]; then - echo "Compiling for macOS." - swift build -Xcc -I/usr/local/include/ -Xlinker -undefined -Xlinker dynamic_lookup && clang -fPIC -dynamiclib -undefined dynamic_lookup .build/debug/Swiftlog.build/Swiftlog.swift.o .build/debug/VPIAssistant.build/VPIAssistant.c.o -o Executables/main.vpi -L$(xcode-select --print-path)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -rpath $(xcode-select --print-path)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -else - echo "Compiling for Linux." - swift build && clang -fPIC -shared .build/debug/Swiftlog.build/*.swift.o .build/debug/VPIAssistant.build/VPIAssistant.c.o -o Executables/main.vpi -L$(dirname $(dirname $(which swift)))/lib/swift/linux -ldispatch -lFoundation -lswiftCore -lswiftGlibc -lswiftRemoteMirror -lswiftSwiftOnoneSupport -rpath $(dirname $(dirname $(which swift)))/lib/swift/linux -fuse-ld=gold -fi \ No newline at end of file diff --git a/run.sh b/run.sh deleted file mode 100755 index fe0cf69..0000000 --- a/run.sh +++ /dev/null @@ -1 +0,0 @@ -vvp -MExecutables/ -mmain Executables/main.vvp \ No newline at end of file diff --git a/simulate b/simulate new file mode 100755 index 0000000..758ce2f --- /dev/null +++ b/simulate @@ -0,0 +1,61 @@ +#!/bin/sh +error=0 + +unamestr=`uname` + +mkdir -p Executables/ + +header=`head -n 1 $1/Startup.swift` + +components=() + +#Portable splitting by: https://stackoverflow.com/a/15988793 +while [ "$header" ] ;do + iterator=${header%%\ *} + components+=("$iterator") + [ "$header" = "$iterator" ] && \ + header='' || \ + header="${header#*\ }" +done + +swiftlogHeader="//SWIFTLOG:" +actualHeader="${components[0]}" + +if [ "$actualHeader" != "$swiftlogHeader" ]; then + echo "Swiftlog verilog header not found in Startup.swift." + exit +fi + +cd $1 +iverilog -o ../Executables/main.vvp "${components[1]}" +cd .. + +error=$((error + $?)) + +# macOS using Mach-style binaries means leaving some symbols hanging like that isn't viable without a special option. +if [ $unamestr = "Darwin" ]; then + echo "Compiling for macOS." + swiftFlags="-Xcc -I/usr/local/include/ -Xlinker -undefined -Xlinker dynamic_lookup" + clangFlags="-dynamiclib -undefined dynamic_lookup" + linkerFlags="-L$(xcode-select --print-path)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -rpath $(xcode-select --print-path)/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx" +else + if [ $unamestr = "Darwin" ]; then + echo "Compiling for Linux." + swiftFlags="" + clangFlags="-shared" + linkerFlags="-L$(dirname $(dirname $(which swift)))/lib/swift/linux -ldispatch -lFoundation -lswiftCore -lswiftGlibc -lswiftRemoteMirror -lswiftSwiftOnoneSupport -rpath $(dirname $(dirname $(which swift)))/lib/swift/linux -fuse-ld=gold" + else + echo "Unsupported OS." + exit 0 + fi +fi + +echo "//Swiftlog Generated File: Do Not Modify\nlet packageName = \"$1\"\n$(cat Package.swiftlog)" > Package.swift + +sh -c "swift build $swiftFlags && clang -fPIC $clangFlags .build/debug/Swiftlog.build/*.swift.o .build/debug/$1.build/*.swift.o .build/debug/VPIAssistant.build/VPIAssistant.c.o -o Executables/main.vpi $linkerFlags" + +error=$((error + $?)) + +if [ "$error" == "0" ]; then + vvp -MExecutables/ -mmain Executables/main.vvp +fi \ No newline at end of file