Skip to content

Commit

Permalink
Swiftlog v0.5
Browse files Browse the repository at this point in the history
+Made Swiftlog much more flexible
+Started on Swift API
+Greatly improved simulation shell script
  • Loading branch information
Skyus committed Aug 12, 2017
1 parent eb647be commit 7732c3a
Show file tree
Hide file tree
Showing 12 changed files with 171 additions and 77 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
build/
DerivedData/
Executables/
Package.pins
Package.pins
Package.swift
1 change: 1 addition & 0 deletions Package.swift → Package.swiftlog
Original file line number Diff line number Diff line change
Expand Up @@ -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")
],
Expand Down
27 changes: 20 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,37 @@ 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: <Verilog File To Simulate Here>
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!
...and Hello from Swift!
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.
Expand All @@ -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'.
Expand Down
92 changes: 69 additions & 23 deletions Swiftlog/Procedure.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import VPIAssistant

typealias PLIInt32 = PLI_INT32

public enum Value: Int
{
case BinaryString = 1
Expand Down Expand Up @@ -77,8 +79,34 @@ public enum ProcedureType: Int
case Func = 2
}



func compiletf(user_data: UnsafeMutablePointer<Int8>?) -> 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<Int8>?) -> 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
Expand All @@ -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
Expand All @@ -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<Int8>?) -> 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<Int8>?) -> Int32 in
return 0
} //See above
self.registered = true
// self.store.compiletf = {
// (user_data: UnsafeMutablePointer<Int8>?) -> 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<Int8>?) -> Int32
func compile() -> PLIInt32
{
guard let handle = vpi_handle(vpiSysTfCall, nil)
else
Expand All @@ -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
}
Expand All @@ -150,22 +186,32 @@ 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
}
}

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)
Expand Down
9 changes: 5 additions & 4 deletions Swiftlog/Utils.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
import Foundation
import VPIAssistant

class Control
public class Control
{
static func finish()
public static func finish()
{
vpi_finish();
}
}

extension String
{
var cPointer: (cLiteral: UnsafePointer<CChar>, cMutable: UnsafeMutablePointer<CChar>, mutable: UnsafeMutablePointer<UInt8>, elementCount: Int)
public var cPointer: (cLiteral: UnsafePointer<CChar>, cMutable: UnsafeMutablePointer<CChar>, mutable: UnsafeMutablePointer<UInt8>, elementCount: Int)
{
let utf8Representation = Array(self.utf8)
var utf8Representation = Array(self.utf8)
utf8Representation.append(0) //0 terminator
let mutablePointer = UnsafeMutablePointer<UInt8>.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)
Expand Down
34 changes: 10 additions & 24 deletions Swiftlog/Swiftlog.swift → SwiftlogExample/Startup.swift
Original file line number Diff line number Diff line change
@@ -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<Int8>?) -> 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<Int8>?) -> 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
Expand Down
2 changes: 1 addition & 1 deletion Verilog/main.v → SwiftlogExample/main.v
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//main Verilog Module
`timescale 1ns/1ns

`include "Verilog/mux.v"
`include "mux.v"

module main;

Expand Down
File renamed without changes.
4 changes: 2 additions & 2 deletions VPIAssistant/VPIAssistant.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down
14 changes: 0 additions & 14 deletions build.sh

This file was deleted.

1 change: 0 additions & 1 deletion run.sh

This file was deleted.

61 changes: 61 additions & 0 deletions simulate
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 7732c3a

Please sign in to comment.