Compare commits
No commits in common. "8c25ce8d214db85656a1303ddc2740a9c6645f27" and "9ae472afafdac147923813e926207871fbb9881f" have entirely different histories.
8c25ce8d21
...
9ae472afaf
@ -1,90 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import struct
|
|
||||||
|
|
||||||
|
|
||||||
def flip32(data: bytes):
|
|
||||||
sl = struct.Struct('<I')
|
|
||||||
sb = struct.Struct('>I')
|
|
||||||
d = bytearray(len(data))
|
|
||||||
for offset in range(0, len(data), 4):
|
|
||||||
sb.pack_into(d, offset, sl.unpack_from(data, offset)[0])
|
|
||||||
return d
|
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
|
||||||
parser = argparse.ArgumentParser(description='Convert FPGA bit files to raw bin format suitable for flashing')
|
|
||||||
parser.add_argument('-f', '--flip', dest='flip', action='store_true', default=False, help='Flip 32-bit endianess (needed for Zynq)')
|
|
||||||
parser.add_argument("bitfiletemplate", help="Input bit file name from which the header is extracted")
|
|
||||||
parser.add_argument("binfile", help="Input bin file name from which the bitstream is extracted")
|
|
||||||
parser.add_argument("bitfile", help="Output bit file name")
|
|
||||||
return parser.parse_args()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
args = parse_args()
|
|
||||||
short = struct.Struct('>H')
|
|
||||||
ulong = struct.Struct('>I')
|
|
||||||
|
|
||||||
bitfile = open(args.bitfiletemplate, 'rb')
|
|
||||||
headerLength = 0
|
|
||||||
l = short.unpack(bitfile.read(2))[0]
|
|
||||||
headerLength = headerLength + 2
|
|
||||||
if l != 9:
|
|
||||||
raise Exception("Missing <0009> header (0x%x), not a bit file" % l)
|
|
||||||
bitfile.read(l)
|
|
||||||
headerLength = headerLength + l
|
|
||||||
l = short.unpack(bitfile.read(2))[0]
|
|
||||||
d = bitfile.read(l)
|
|
||||||
headerLength = headerLength + l + 2
|
|
||||||
if d != b'a':
|
|
||||||
raise Exception("Missing <a> header, not a bit file")
|
|
||||||
|
|
||||||
l = short.unpack(bitfile.read(2))[0]
|
|
||||||
d = bitfile.read(l)
|
|
||||||
headerLength = headerLength + l +2
|
|
||||||
print("Design name: %s" % d)
|
|
||||||
|
|
||||||
KEYNAMES = {b'b': "Partname", b'c': "Date", b'd': "Time"}
|
|
||||||
|
|
||||||
while 1:
|
|
||||||
k = bitfile.read(1)
|
|
||||||
headerLength = headerLength + 1
|
|
||||||
if not k:
|
|
||||||
bitfile.close()
|
|
||||||
raise Exception("unexpected EOF")
|
|
||||||
elif k == b'e':
|
|
||||||
l = ulong.unpack(bitfile.read(4))[0]
|
|
||||||
headerLength = headerLength + 4
|
|
||||||
print("Found header end: %d" % headerLength)
|
|
||||||
break
|
|
||||||
elif k in KEYNAMES:
|
|
||||||
l = short.unpack(bitfile.read(2))[0]
|
|
||||||
d = bitfile.read(l)
|
|
||||||
headerLength = headerLength + l + 2
|
|
||||||
print(KEYNAMES[k], d)
|
|
||||||
else:
|
|
||||||
print("Unexpected key: %s" % k)
|
|
||||||
l = short.unpack(bitfile.read(2))[0]
|
|
||||||
d = bitfile.read(l)
|
|
||||||
headerLength = headerLength + l + 2
|
|
||||||
|
|
||||||
outputfile = open(args.bitfile, 'wb+')
|
|
||||||
bitfile.seek(0)
|
|
||||||
outputfile.write(bitfile.read(headerLength))
|
|
||||||
bitfile.close()
|
|
||||||
|
|
||||||
binfile = open( args.binfile, 'rb')
|
|
||||||
d = binfile.read()
|
|
||||||
binfile.close()
|
|
||||||
if args.flip:
|
|
||||||
print("Flipping data...")
|
|
||||||
d = flip32(d)
|
|
||||||
|
|
||||||
outputfile.write(d)
|
|
||||||
outputfile.close()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
119
arm/async.cc
119
arm/async.cc
@ -5,9 +5,22 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "lock.h"
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
#ifdef __x86_64__
|
||||||
|
#include <mutex>
|
||||||
|
struct InterruptLock {
|
||||||
|
static std::mutex m;
|
||||||
|
InterruptLock() { m.lock(); }
|
||||||
|
~InterruptLock() { m.unlock(); }
|
||||||
|
};
|
||||||
|
std::mutex InterruptLock::m;
|
||||||
|
#else // __x86_64__
|
||||||
|
#include "lock.h"
|
||||||
|
|
||||||
|
using tracing::trace;
|
||||||
|
#endif // __x86_64__
|
||||||
|
|
||||||
namespace async {
|
namespace async {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
@ -25,14 +38,13 @@ struct Notification {
|
|||||||
Stuff* stuff;
|
Stuff* stuff;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::atomic<Stuff*> work = nullptr;
|
std::atomic<Stuff*> work;
|
||||||
std::array<Notification, static_cast<size_t>(AwaitableType::kNumTypes)>
|
std::array<Notification, static_cast<size_t>(AwaitableType::kNumTypes)>
|
||||||
notifications = {};
|
notifications = {};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void schedule(std::coroutine_handle<> h, int ms) {
|
void schedule(std::coroutine_handle<> h, int ms) {
|
||||||
InterruptLock lock;
|
|
||||||
TRACE(tracing::TraceEvent::kAsyncSchedule);
|
TRACE(tracing::TraceEvent::kAsyncSchedule);
|
||||||
std::chrono::system_clock::time_point exp =
|
std::chrono::system_clock::time_point exp =
|
||||||
std::chrono::system_clock::now() + std::chrono::milliseconds(ms);
|
std::chrono::system_clock::now() + std::chrono::milliseconds(ms);
|
||||||
@ -58,64 +70,38 @@ void schedule(std::coroutine_handle<> h, int ms) {
|
|||||||
s->next = news;
|
s->next = news;
|
||||||
}
|
}
|
||||||
|
|
||||||
void step() {
|
|
||||||
Stuff* stuff;
|
|
||||||
// ensure all previous side effects are visible
|
|
||||||
{
|
|
||||||
InterruptLock lock;
|
|
||||||
stuff = work;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (stuff == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto now = std::chrono::system_clock::now();
|
|
||||||
auto dt = stuff->expiration - now;
|
|
||||||
|
|
||||||
if (dt > 0ms) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int stuffinqueue = 0;
|
|
||||||
for (Stuff* s = stuff; s; s = s->next) stuffinqueue++;
|
|
||||||
|
|
||||||
TRACE(tracing::TraceEvent::kAsyncTask);
|
|
||||||
stuff->h();
|
|
||||||
TRACE(tracing::TraceEvent::kAsyncTaskDone);
|
|
||||||
|
|
||||||
if (stuff->h.done()) {
|
|
||||||
stuff->h.destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
InterruptLock lock;
|
|
||||||
work = stuff->next;
|
|
||||||
}
|
|
||||||
delete stuff;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset() {
|
|
||||||
Stuff* stuff = work;
|
|
||||||
while (stuff) {
|
|
||||||
Stuff* byebye = stuff;
|
|
||||||
stuff = stuff->next;
|
|
||||||
|
|
||||||
delete byebye;
|
|
||||||
}
|
|
||||||
work = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void main_loop(bool (*idle_function)()) {
|
void main_loop(bool (*idle_function)()) {
|
||||||
while (1) {
|
while (1) {
|
||||||
if (idle_function != nullptr) {
|
if (idle_function != nullptr) {
|
||||||
if (idle_function()) {
|
if (idle_function()) {
|
||||||
reset();
|
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Stuff* stuff = work;
|
||||||
|
if (stuff == nullptr) {
|
||||||
|
continue; // busyloop
|
||||||
|
}
|
||||||
|
|
||||||
step();
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto dt = stuff->expiration - now;
|
||||||
|
|
||||||
|
if (dt > 0ms) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE(tracing::TraceEvent::kAsyncTask);
|
||||||
|
stuff->h();
|
||||||
|
TRACE(tracing::TraceEvent::kAsyncTaskDone);
|
||||||
|
|
||||||
|
if (stuff->h.done()) {
|
||||||
|
stuff->h.destroy();
|
||||||
|
}
|
||||||
|
{
|
||||||
|
InterruptLock lock;
|
||||||
|
|
||||||
|
work = stuff->next;
|
||||||
|
}
|
||||||
|
delete stuff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,3 +152,30 @@ void resume(AwaitableType type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace async
|
} // namespace async
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
task<buffer> readline() {
|
||||||
|
int size = 0;
|
||||||
|
char c;
|
||||||
|
|
||||||
|
buffer buff = buffer::make(32);
|
||||||
|
|
||||||
|
// fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int n = read(0, &c, 1);
|
||||||
|
if (n < 1) {
|
||||||
|
co_await co_waitio();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buff.data[size++] = static_cast<std::byte>(c);
|
||||||
|
|
||||||
|
if (c == '\n') {
|
||||||
|
buff.data = buff.data.subspan(0, size);
|
||||||
|
co_return buff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // 0
|
||||||
|
49
arm/async.h
49
arm/async.h
@ -1,11 +1,19 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <coroutine>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#include <experimental/coroutine>
|
||||||
|
namespace std {
|
||||||
|
using namespace experimental;
|
||||||
|
}
|
||||||
|
#else // __clang__
|
||||||
|
#include <coroutine>
|
||||||
|
#endif // __clang__
|
||||||
|
|
||||||
namespace async {
|
namespace async {
|
||||||
|
|
||||||
struct task_final_suspend {
|
struct task_final_suspend {
|
||||||
@ -15,11 +23,6 @@ struct task_final_suspend {
|
|||||||
TRACE(tracing::TraceEvent::kAsyncCallParent);
|
TRACE(tracing::TraceEvent::kAsyncCallParent);
|
||||||
parent();
|
parent();
|
||||||
TRACE(tracing::TraceEvent::kAsyncCallParentDone);
|
TRACE(tracing::TraceEvent::kAsyncCallParentDone);
|
||||||
|
|
||||||
if (parent && parent.done()) {
|
|
||||||
TRACE(tracing::TraceEvent::kAsyncDestroy);
|
|
||||||
parent.destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void await_resume() noexcept(true) {}
|
void await_resume() noexcept(true) {}
|
||||||
@ -35,6 +38,12 @@ struct gimme {
|
|||||||
ha = h;
|
ha = h;
|
||||||
waiting = true;
|
waiting = true;
|
||||||
TRACE(tracing::TraceEvent::kAsyncGimmeWaiting);
|
TRACE(tracing::TraceEvent::kAsyncGimmeWaiting);
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
TRACE(tracing::TraceEvent::kAsyncCallParent);
|
||||||
|
parent.resume();
|
||||||
|
TRACE(tracing::TraceEvent::kAsyncCallParentDone);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
T await_resume() {
|
T await_resume() {
|
||||||
waiting = false;
|
waiting = false;
|
||||||
@ -43,7 +52,23 @@ struct gimme {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parent interface
|
// parent interface
|
||||||
void feed(T&& s) {
|
auto feed(T&& s) {
|
||||||
|
struct awaitable {
|
||||||
|
bool await_ready() {
|
||||||
|
if (g.waiting) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// TODO: handle g.ha.done()
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
void await_suspend(std::coroutine_handle<> h) {
|
||||||
|
g.parent = h;
|
||||||
|
}
|
||||||
|
void await_resume() {}
|
||||||
|
|
||||||
|
gimme<T>& g;
|
||||||
|
};
|
||||||
|
|
||||||
if (!waiting) {
|
if (!waiting) {
|
||||||
__builtin_trap();
|
__builtin_trap();
|
||||||
}
|
}
|
||||||
@ -51,12 +76,16 @@ struct gimme {
|
|||||||
__builtin_trap();
|
__builtin_trap();
|
||||||
}
|
}
|
||||||
stuff = s;
|
stuff = s;
|
||||||
|
parent = {};
|
||||||
ha.resume();
|
ha.resume();
|
||||||
|
|
||||||
|
return awaitable{.g = *this};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool waiting = false;
|
bool waiting = false;
|
||||||
std::coroutine_handle<> ha;
|
std::coroutine_handle<> ha;
|
||||||
T stuff;
|
T stuff;
|
||||||
|
std::coroutine_handle<> parent;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T = void>
|
template <typename T = void>
|
||||||
@ -88,8 +117,7 @@ struct task<void> {
|
|||||||
TRACE(tracing::TraceEvent::kAsyncCoAwait);
|
TRACE(tracing::TraceEvent::kAsyncCoAwait);
|
||||||
h();
|
h();
|
||||||
if (h.done()) {
|
if (h.done()) {
|
||||||
TRACE(tracing::TraceEvent::kAsyncDestroy);
|
TRACE(tracing::TraceEvent::kAsyncDestroy); h.destroy();
|
||||||
h.destroy();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -177,8 +205,7 @@ void schedule(std::coroutine_handle<> h, int ms = 0);
|
|||||||
void enqueue(std::coroutine_handle<> h, AwaitableType type);
|
void enqueue(std::coroutine_handle<> h, AwaitableType type);
|
||||||
void resume(AwaitableType type); // typically called from an ISR
|
void resume(AwaitableType type); // typically called from an ISR
|
||||||
|
|
||||||
void main_loop(bool (*idle_function)());
|
void main_loop(void (*idle_function)());
|
||||||
void step();
|
|
||||||
|
|
||||||
inline auto await(AwaitableType type) {
|
inline auto await(AwaitableType type) {
|
||||||
struct awaitable {
|
struct awaitable {
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <mutex>
|
|
||||||
#include <semaphore>
|
#include <semaphore>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@ -29,14 +28,11 @@ int uptime() {
|
|||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
void log(int i, const char* fmt, Args... args) {
|
void log(int i, const char* fmt, Args... args) {
|
||||||
if (i != 3) {
|
if (i != 4) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
printf("[%10i] ", uptime());
|
printf("[%10i] ", uptime());
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma clang diagnostic ignored "-Wformat-security"
|
|
||||||
printf(fmt, args...);
|
printf(fmt, args...);
|
||||||
#pragma clang diagnostic pop
|
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,7 +124,6 @@ TEST_F(LoopAsyncTest, ManyEnqueueResume) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EXPECT_EQ(done, kLoops);
|
EXPECT_EQ(done, kLoops);
|
||||||
log(3, "got all of em, done");
|
|
||||||
|
|
||||||
terminate = true;
|
terminate = true;
|
||||||
t.join();
|
t.join();
|
||||||
@ -201,14 +196,10 @@ std::binary_semaphore NestedAsync::next(0);
|
|||||||
std::binary_semaphore NestedAsync::ready(0);
|
std::binary_semaphore NestedAsync::ready(0);
|
||||||
|
|
||||||
TEST_F(NestedAsync, SimpleNestedTest) {
|
TEST_F(NestedAsync, SimpleNestedTest) {
|
||||||
while (next.try_acquire());
|
|
||||||
while (ready.try_acquire());
|
|
||||||
|
|
||||||
async::schedule(nested().h);
|
async::schedule(nested().h);
|
||||||
std::thread t([]() {
|
std::thread t([]() {
|
||||||
async::main_loop([]() -> bool {
|
async::main_loop([]() -> bool {
|
||||||
// for some reason the bare `acquire` fails sometimes :/
|
next.acquire();
|
||||||
while (!next.try_acquire_for(10ms));
|
|
||||||
return terminate;
|
return terminate;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -231,23 +222,39 @@ TEST_F(NestedAsync, SimpleNestedTest) {
|
|||||||
|
|
||||||
TEST_F(NestedAsync, LessSimpleNestedTest) {
|
TEST_F(NestedAsync, LessSimpleNestedTest) {
|
||||||
async::schedule(othernested().h);
|
async::schedule(othernested().h);
|
||||||
|
std::thread t([]() {
|
||||||
|
async::main_loop([]() -> bool {
|
||||||
|
ready.release();
|
||||||
|
log(3, "loop ready");
|
||||||
|
next.acquire();
|
||||||
|
return terminate;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
ASSERT_FALSE(voiddone);
|
ASSERT_FALSE(voiddone);
|
||||||
ASSERT_EQ(result, 0);
|
ASSERT_EQ(result, 0);
|
||||||
|
|
||||||
log(3, "step");
|
log(3, "starting stuff");
|
||||||
async::step(); // should proceed until yield
|
|
||||||
|
ASSERT_TRUE(ready.try_acquire_for(1s));
|
||||||
|
next.release();
|
||||||
|
ASSERT_TRUE(ready.try_acquire_for(1s));
|
||||||
|
|
||||||
ASSERT_FALSE(voiddone);
|
ASSERT_FALSE(voiddone);
|
||||||
ASSERT_EQ(result, 0);
|
ASSERT_EQ(result, 0);
|
||||||
|
|
||||||
log(3, "step");
|
next.release();
|
||||||
async::step(); // should proceed until end
|
ASSERT_TRUE(ready.try_acquire_for(1s));
|
||||||
|
|
||||||
EXPECT_EQ(voiddone, true);
|
EXPECT_EQ(voiddone, true);
|
||||||
ASSERT_EQ(result, 42);
|
ASSERT_EQ(result, 42);
|
||||||
|
|
||||||
EXPECT_TRUE(done.try_acquire_for(1s));
|
EXPECT_TRUE(done.try_acquire_for(1s));
|
||||||
|
|
||||||
|
terminate = true;
|
||||||
|
next.release();
|
||||||
|
|
||||||
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(NestedAsync, GeneratorTest) {
|
TEST_F(NestedAsync, GeneratorTest) {
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
#include "gpio.h"
|
#include "gpio.h"
|
||||||
#include "itoa.h"
|
#include "itoa.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#include "trace.h"
|
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
|
|
||||||
extern "C" uint32_t _initial_stack_pointer, _text_begin, _text_end;
|
extern "C" uint32_t _initial_stack_pointer, _text_begin, _text_end;
|
||||||
@ -81,8 +80,6 @@ void CrashHandler(Armv6mRegs* regs) {
|
|||||||
UartWriteCrash("- Stack trace:\r\n");
|
UartWriteCrash("- Stack trace:\r\n");
|
||||||
StackTrace(reinterpret_cast<uint32_t*>(regs->sp));
|
StackTrace(reinterpret_cast<uint32_t*>(regs->sp));
|
||||||
|
|
||||||
tracing::dump();
|
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
gpio0->data = 0x55;
|
gpio0->data = 0x55;
|
||||||
sleep(100);
|
sleep(100);
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
FROM ubuntu
|
|
||||||
|
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
|
|
||||||
apt-get install -y make clang libgmock-dev gdb && \
|
|
||||||
apt-get clean && rm -rf /var/lib/apt/lists
|
|
202
arm/fake_uart.cc
202
arm/fake_uart.cc
@ -1,202 +0,0 @@
|
|||||||
#include "uart.h"
|
|
||||||
|
|
||||||
#include "async.h"
|
|
||||||
#include "buffer.h"
|
|
||||||
#include "ring_buffer.h"
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <mutex>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
using async::AwaitableType;
|
|
||||||
|
|
||||||
constexpr size_t kUartTxBufferSize = 256;
|
|
||||||
std::array<std::byte, kUartTxBufferSize> tx_buffer = {};
|
|
||||||
RingBuffer tx_ring_buffer{.buffer = tx_buffer};
|
|
||||||
|
|
||||||
uint8_t* tx_buff = nullptr;
|
|
||||||
uint16_t tx_size = 0;
|
|
||||||
uint16_t tx_size_orig = 0;
|
|
||||||
|
|
||||||
uint8_t* rx_buff = nullptr;
|
|
||||||
uint16_t rx_size = 0;
|
|
||||||
uint16_t rx_size_orig = 0;
|
|
||||||
std::vector<std::byte> rx_overflow;
|
|
||||||
|
|
||||||
std::mutex interrupt_lock;
|
|
||||||
std::mutex fu_mutex;
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
//// Internal API
|
|
||||||
void FakeUart_Send(uint8_t* ptr, uint16_t size) {
|
|
||||||
std::scoped_lock lock(fu_mutex);
|
|
||||||
|
|
||||||
tx_buff = ptr;
|
|
||||||
tx_size = size;
|
|
||||||
tx_size_orig = size;
|
|
||||||
}
|
|
||||||
size_t FakeUart_Recv(uint8_t* ptr, uint16_t size) {
|
|
||||||
std::scoped_lock lock(fu_mutex);
|
|
||||||
|
|
||||||
if (size <= rx_overflow.size()) {
|
|
||||||
std::copy(rx_overflow.begin(), rx_overflow.begin() + size,
|
|
||||||
std::as_writable_bytes(std::span{ptr, size}).begin());
|
|
||||||
std::rotate(rx_overflow.begin(), rx_overflow.begin() + size, rx_overflow.end());
|
|
||||||
rx_overflow.resize(rx_overflow.size() - size);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
rx_buff = ptr;
|
|
||||||
rx_size = size;
|
|
||||||
|
|
||||||
if (rx_overflow.empty()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::copy(rx_overflow.begin(), rx_overflow.end(),
|
|
||||||
std::as_writable_bytes(std::span{ptr, size}).begin());
|
|
||||||
size_t copied = rx_overflow.size();
|
|
||||||
rx_overflow.clear();
|
|
||||||
|
|
||||||
rx_buff += copied;
|
|
||||||
rx_size -= copied;
|
|
||||||
|
|
||||||
return copied;
|
|
||||||
}
|
|
||||||
uint8_t FakeUart_IsSending() {
|
|
||||||
std::scoped_lock lock(fu_mutex);
|
|
||||||
return tx_size > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//// Backdoor access API
|
|
||||||
/// Those do trigger UART interrupts when tx/rx buffer state changes
|
|
||||||
buffer FakeUart_Drain(uint16_t size) {
|
|
||||||
std::scoped_lock isr_lock(interrupt_lock); // not really the same, but that'll do
|
|
||||||
std::scoped_lock lock(fu_mutex);
|
|
||||||
|
|
||||||
size_t drained = std::min(size, tx_size);
|
|
||||||
|
|
||||||
if (drained < 1) {
|
|
||||||
return buffer{};
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer buff = buffer::make(drained);
|
|
||||||
auto txb = std::as_bytes(std::span{tx_buff, tx_size});
|
|
||||||
std::copy(txb.begin(), txb.begin() + drained, buff.data.begin());
|
|
||||||
|
|
||||||
tx_buff += drained;
|
|
||||||
tx_size -= drained;
|
|
||||||
|
|
||||||
if (tx_size < 1) {
|
|
||||||
fu_mutex.unlock();
|
|
||||||
HandleUartTxFromIsr(nullptr, tx_size_orig);
|
|
||||||
fu_mutex.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
return buff;
|
|
||||||
}
|
|
||||||
void FakeUart_Feed(std::span<const std::byte> data) {
|
|
||||||
std::scoped_lock lock(fu_mutex);
|
|
||||||
|
|
||||||
if (data.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto rxb = std::as_writable_bytes(std::span{rx_buff, rx_size});
|
|
||||||
size_t fed = std::min(static_cast<size_t>(rx_size), data.size());
|
|
||||||
|
|
||||||
if (data.size() > fed) {
|
|
||||||
rx_overflow.insert(rx_overflow.end(), data.begin() + fed, data.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fed > 0) {
|
|
||||||
std::copy(data.begin(), data.begin() + fed, rxb.begin());
|
|
||||||
|
|
||||||
rx_buff += fed;
|
|
||||||
rx_size -= fed;
|
|
||||||
|
|
||||||
if (rx_size < 1) {
|
|
||||||
HandleUartRxFromIsr(nullptr, rx_size_orig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void FakeUart_Reset() {
|
|
||||||
rx_overflow.clear();
|
|
||||||
|
|
||||||
rx_buff = nullptr;
|
|
||||||
rx_size = 0;
|
|
||||||
tx_buff = nullptr;
|
|
||||||
tx_size = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//// Public API
|
|
||||||
void InitUarts() {}
|
|
||||||
void UartWriteCrash(std::span<const std::byte>) {}
|
|
||||||
void LogStuff() {}
|
|
||||||
void UartReadBlocking(std::span<std::byte>) {}
|
|
||||||
void UartWriteBlocking(std::span<const std::byte>) {}
|
|
||||||
void HandleUartIsr() {}
|
|
||||||
async::task<buffer> UartRead(int size) {
|
|
||||||
co_return buffer{};
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleUartTxFromIsr(void*, unsigned int transmitted) {
|
|
||||||
tx_ring_buffer.Pop(transmitted);
|
|
||||||
if (tx_ring_buffer.AvailableData() > 0 && !FakeUart_IsSending()) {
|
|
||||||
FakeUart_Send(tx_ring_buffer.RawReadPointer(),
|
|
||||||
tx_ring_buffer.ContiguousAvailableData());
|
|
||||||
}
|
|
||||||
async::resume(AwaitableType::kUartTx);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleUartRxFromIsr(void*, unsigned int) {
|
|
||||||
async::resume(AwaitableType::kUartRx);
|
|
||||||
}
|
|
||||||
|
|
||||||
async::task<> UartWrite(std::span<const std::byte> data) {
|
|
||||||
while (!tx_ring_buffer.Store(data)) {
|
|
||||||
co_await async::await(AwaitableType::kUartTx);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(interrupt_lock);
|
|
||||||
|
|
||||||
if (!FakeUart_IsSending()) {
|
|
||||||
FakeUart_Send(tx_ring_buffer.RawReadPointer(),
|
|
||||||
tx_ring_buffer.ContiguousAvailableData());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async::task<> UartWriteLoop(async::gimme<std::span<const std::byte>>& data_gen) {
|
|
||||||
while (1) {
|
|
||||||
auto data = co_await data_gen;
|
|
||||||
while (!tx_ring_buffer.Store(data)) {
|
|
||||||
co_await async::await(AwaitableType::kUartTx);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
std::scoped_lock lock(interrupt_lock);
|
|
||||||
|
|
||||||
if (tx_ring_buffer.AvailableData() && !FakeUart_IsSending()) {
|
|
||||||
FakeUart_Send(tx_ring_buffer.RawReadPointer(),
|
|
||||||
tx_ring_buffer.ContiguousAvailableData());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async::task<uint8_t> UartReadLoop() {
|
|
||||||
uint8_t c;
|
|
||||||
while (1) {
|
|
||||||
size_t received = FakeUart_Recv(&c, 1);
|
|
||||||
// some data may already be in the fifo, but if not, wait:
|
|
||||||
if (received < 1) {
|
|
||||||
co_await async::await(AwaitableType::kUartRx);
|
|
||||||
}
|
|
||||||
|
|
||||||
co_yield c;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <span>
|
|
||||||
|
|
||||||
#include "buffer.h"
|
|
||||||
|
|
||||||
buffer FakeUart_Drain(uint16_t size);
|
|
||||||
void FakeUart_Feed(std::span<const std::byte> data);
|
|
||||||
void FakeUart_Reset();
|
|
@ -1,5 +0,0 @@
|
|||||||
#include "lock.h"
|
|
||||||
|
|
||||||
#ifdef __x86_64__
|
|
||||||
std::recursive_mutex InterruptLock::m;
|
|
||||||
#endif
|
|
18
arm/lock.h
18
arm/lock.h
@ -1,23 +1,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#ifndef __x86_64__
|
|
||||||
#include "aum1_cm1.h"
|
#include "aum1_cm1.h"
|
||||||
|
|
||||||
struct InterruptLock {
|
struct InterruptLock {
|
||||||
uint32_t old_primask;
|
bool was_locked;
|
||||||
|
|
||||||
InterruptLock() : old_primask(__get_PRIMASK()) { __disable_irq(); }
|
InterruptLock() : was_locked(__get_PRIMASK() != 0) { __disable_irq(); }
|
||||||
|
|
||||||
~InterruptLock() {
|
~InterruptLock() {
|
||||||
__set_PRIMASK(old_primask);
|
if (!was_locked) {
|
||||||
|
__enable_irq();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
#else // __x86_64__
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
struct InterruptLock {
|
|
||||||
static std::recursive_mutex m;
|
|
||||||
InterruptLock() { m.lock(); }
|
|
||||||
~InterruptLock() { m.unlock(); }
|
|
||||||
};
|
|
||||||
#endif // __x86_64__
|
|
60
arm/main.cc
60
arm/main.cc
@ -14,12 +14,14 @@ namespace {
|
|||||||
using async::AwaitableType;
|
using async::AwaitableType;
|
||||||
|
|
||||||
void Uart0Isr() {
|
void Uart0Isr() {
|
||||||
ToggleLed(7);
|
tracing::trace(tracing::TraceEvent::kUartIsr);
|
||||||
//tracing::trace(tracing::TraceEvent::kUartIsr);
|
|
||||||
HandleUartIsr();
|
HandleUartIsr();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timer0Isr() { __builtin_trap(); }
|
void Timer0Isr() {
|
||||||
|
tracing::dump();
|
||||||
|
__builtin_trap();
|
||||||
|
}
|
||||||
|
|
||||||
void SetupUart() {
|
void SetupUart() {
|
||||||
InitUarts();
|
InitUarts();
|
||||||
@ -30,81 +32,35 @@ void SetupUart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetupTimer() {
|
void SetupTimer() {
|
||||||
timer0->SetupAsWdt(100000 * 1); //4000);
|
timer0->SetupAsWdt(100000 * 4000);
|
||||||
timer0->EnableT1();
|
timer0->EnableT1();
|
||||||
|
|
||||||
_vector_table[16 + Timer0_IRQn] = reinterpret_cast<uint32_t>(Timer0Isr);
|
_vector_table[16 + Timer0_IRQn] = reinterpret_cast<uint32_t>(Timer0Isr);
|
||||||
NVIC_EnableIRQ(Timer0_IRQn);
|
NVIC_EnableIRQ(Timer0_IRQn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if 1 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101133
|
|
||||||
async::task<> echo() {
|
async::task<> echo() {
|
||||||
async::task<uint8_t> reader = UartReadLoop();
|
|
||||||
async::gimme<std::span<const std::byte>> feeder;
|
|
||||||
async::task<> writer = UartWriteLoop(feeder);
|
|
||||||
writer.h.resume(); // advance to first yield
|
|
||||||
while (1) {
|
while (1) {
|
||||||
SetLed(1);
|
buffer buff = co_await UartRead(1);
|
||||||
uint8_t c = co_await reader;
|
co_await UartWrite(buff.data);
|
||||||
ClearLed(1);
|
|
||||||
ToggleLed(2);
|
|
||||||
feeder.feed(std::as_bytes(std::span{&c, 1}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
async::task<> echo() {
|
|
||||||
async::task<uint8_t> reader = UartReadLoop();
|
|
||||||
while (1) {
|
|
||||||
SetLed(1);
|
|
||||||
uint8_t c = co_await reader;
|
|
||||||
ClearLed(1);
|
|
||||||
ToggleLed(2);
|
|
||||||
co_await UartWrite(std::as_bytes(std::span{&c, 1}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
[[maybe_unused]]
|
|
||||||
async::task<> dump_trace() {
|
|
||||||
while (1) {
|
|
||||||
co_await async::delay(5000);
|
|
||||||
// tracing::dump();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#define XUL_SR_RX_FIFO_FULL 0x02 /* receive FIFO full */
|
|
||||||
#define XUL_SR_RX_FIFO_VALID_DATA 0x01 /* data in receive FIFO */
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
_vector_table[16 + HardFault_IRQn] =
|
_vector_table[16 + HardFault_IRQn] =
|
||||||
reinterpret_cast<uint32_t>(crash::HardFaultHandler);
|
reinterpret_cast<uint32_t>(crash::HardFaultHandler);
|
||||||
|
|
||||||
SetupUart();
|
SetupUart();
|
||||||
UartWriteBlocking("uart setup done\r\n");
|
|
||||||
SetupTimer();
|
SetupTimer();
|
||||||
UartWriteBlocking("timer setup done\r\n");
|
|
||||||
|
|
||||||
async::schedule(echo().h);
|
async::schedule(echo().h);
|
||||||
// async::schedule(dump_trace().h);
|
|
||||||
|
|
||||||
tracing::trace(tracing::TraceEvent::kUartIsr);
|
|
||||||
tracing::trace(tracing::TraceEvent::kUartIsr);
|
|
||||||
|
|
||||||
UartWriteBlocking("init done. starting main loop\r\n");
|
|
||||||
|
|
||||||
async::main_loop([]() {
|
async::main_loop([]() {
|
||||||
static int cnt = 0;
|
static int cnt = 0;
|
||||||
timer0->Pet();
|
timer0->Pet();
|
||||||
if ((cnt++ % 100000) == 0) {
|
if ((cnt++ % 100000) == 0) {
|
||||||
ToggleLed(0);
|
ToggleLed(0);
|
||||||
ClearLed(7);
|
|
||||||
ClearLed(2);
|
|
||||||
ClearLed(3);
|
|
||||||
ClearLed(4);
|
|
||||||
|
|
||||||
LogStuff();
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
43
arm/makefile
43
arm/makefile
@ -43,7 +43,10 @@ app.elf: $(app_objects) app.ld
|
|||||||
|
|
||||||
deps = $(app_objects:.o=.d) $(bootloader_objects:.o=.d)
|
deps = $(app_objects:.o=.d) $(bootloader_objects:.o=.d)
|
||||||
|
|
||||||
HOSTCXX = clang++
|
clean:
|
||||||
|
rm -rf *.elf *.bin $(tests) $(app_objects) $(bootloader_objects) $(deps)
|
||||||
|
|
||||||
|
HOSTCXX = /usr/local/opt/llvm/bin/clang++
|
||||||
HOSTLDFLAGS = -lgmock -lgtest -lgtest_main -L/usr/local/opt/llvm/lib -L/usr/local/lib
|
HOSTLDFLAGS = -lgmock -lgtest -lgtest_main -L/usr/local/opt/llvm/lib -L/usr/local/lib
|
||||||
HOSTCFLAGS = -std=c++20 -g\
|
HOSTCFLAGS = -std=c++20 -g\
|
||||||
-I/usr/local/opt/llvm/include \
|
-I/usr/local/opt/llvm/include \
|
||||||
@ -54,39 +57,31 @@ HOSTCFLAGS = -std=c++20 -g\
|
|||||||
TSAN_CFLAGS = $(HOSTCFLAGS) -fsanitize=thread
|
TSAN_CFLAGS = $(HOSTCFLAGS) -fsanitize=thread
|
||||||
ASAN_CFLAGS = $(HOSTCFLAGS) -fsanitize=address -fsanitize=leak
|
ASAN_CFLAGS = $(HOSTCFLAGS) -fsanitize=address -fsanitize=leak
|
||||||
|
|
||||||
tests = ring_buffer_test.run async_test_asan.run async_test_tsan.run uart_test_tsan.run
|
tests = ring_buffer_test async_test_asan async_test_tsan
|
||||||
|
|
||||||
|
.PRECIOUS: $(tests)
|
||||||
|
|
||||||
test: $(tests)
|
test: $(tests)
|
||||||
|
|
||||||
test/async_test_asan: async_test.cc async.host.o lock.host.o
|
ring_buffer_test: ring_buffer_test.cc
|
||||||
test/async_test_tsan: async_test.cc async.host.o lock.host.o
|
mkdir test
|
||||||
test/ring_buffer_test: ring_buffer_test.cc lock.host.o
|
$(HOSTCXX) $(HOSTCFLAGS) -o test/$@ $< $(HOSTLDFLAGS)
|
||||||
test/uart_test_tsan: uart_test.cc fake_uart.host.o async.host.o lock.host.o
|
./test/$@
|
||||||
|
|
||||||
%.run: test/%
|
|
||||||
TSAN_OPTIONS='suppressions=tsan.suppressions' ASAN_OPTIONS=detect_leaks=1 ./$<
|
|
||||||
|
|
||||||
%.host.o: %.cc
|
%.host.o: %.cc
|
||||||
$(HOSTCXX) $(HOSTCFLAGS) -c -o $@ $<
|
$(HOSTCXX) $(HOSTCFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
test/%_test: | mktest
|
async_test_tsan: async_test.cc async.host.o
|
||||||
$(HOSTCXX) $(HOSTCFLAGS) -o $@ $^ $(HOSTLDFLAGS)
|
|
||||||
|
|
||||||
test/%_asan: | mktest
|
|
||||||
$(HOSTCXX) $(ASAN_CFLAGS) -o $@ $^ $(HOSTLDFLAGS)
|
|
||||||
|
|
||||||
test/%_tsan: | mktest
|
|
||||||
$(HOSTCXX) $(TSAN_CFLAGS) -o $@ $^ $(HOSTLDFLAGS)
|
|
||||||
|
|
||||||
mktest:
|
|
||||||
mkdir -p test
|
mkdir -p test
|
||||||
|
$(HOSTCXX) $(TSAN_CFLAGS) -o test/$@ $^ $(HOSTLDFLAGS)
|
||||||
|
TSAN_OPTIONS='suppressions=tsan.suppressions' ./test/$@
|
||||||
|
|
||||||
test_deps = async.host.d lock.host.d fake_uart.host.d
|
async_test_asan: async_test.cc async.host.o
|
||||||
|
mkdir -p test
|
||||||
|
$(HOSTCXX) $(ASAN_CFLAGS) -o test/$@ $^ $(HOSTLDFLAGS)
|
||||||
|
ASAN_OPTIONS=detect_leaks=1 ./test/$@
|
||||||
|
|
||||||
|
test_deps = async.host.d
|
||||||
clean:
|
|
||||||
rm -rf *.elf *.bin $(app_objects) $(bootloader_objects) $(deps)
|
|
||||||
rm -rf test/ *.dSYM $(test_deps) *.o
|
|
||||||
|
|
||||||
-include $(deps)
|
-include $(deps)
|
||||||
-include $(test_deps)
|
-include $(test_deps)
|
12
arm/prog.py
12
arm/prog.py
@ -1,7 +1,6 @@
|
|||||||
import serial
|
import serial
|
||||||
import struct
|
import struct
|
||||||
import sys
|
import sys
|
||||||
import time
|
|
||||||
|
|
||||||
offset = 0x800
|
offset = 0x800
|
||||||
tty = 'tty'
|
tty = 'tty'
|
||||||
@ -42,17 +41,6 @@ def main():
|
|||||||
s = serial.Serial(tty, baud, timeout=1)
|
s = serial.Serial(tty, baud, timeout=1)
|
||||||
write(s, offset, dat)
|
write(s, offset, dat)
|
||||||
jump(s, offset)
|
jump(s, offset)
|
||||||
last_dat = time.time()
|
|
||||||
|
|
||||||
while True:
|
|
||||||
dat = s.read()
|
|
||||||
if not dat:
|
|
||||||
if time.time() - last_dat > 1.0:
|
|
||||||
print('Data timeout')
|
|
||||||
break
|
|
||||||
continue
|
|
||||||
last_dat = time.time()
|
|
||||||
sys.stdout.buffer.write(dat)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -3,8 +3,6 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
|
||||||
#include "lock.h"
|
|
||||||
|
|
||||||
struct RingBuffer {
|
struct RingBuffer {
|
||||||
std::span<std::byte> buffer;
|
std::span<std::byte> buffer;
|
||||||
|
|
||||||
@ -13,8 +11,6 @@ struct RingBuffer {
|
|||||||
std::atomic<bool> full = 0;
|
std::atomic<bool> full = 0;
|
||||||
|
|
||||||
bool Store(std::span<const std::byte> data) {
|
bool Store(std::span<const std::byte> data) {
|
||||||
InterruptLock lock;
|
|
||||||
|
|
||||||
if (data.size() > FreeSpace()) {
|
if (data.size() > FreeSpace()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -30,8 +26,6 @@ struct RingBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Load(std::span<std::byte> out) {
|
bool Load(std::span<std::byte> out) {
|
||||||
InterruptLock lock;
|
|
||||||
|
|
||||||
if (out.size() > AvailableData()) {
|
if (out.size() > AvailableData()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -47,8 +41,6 @@ struct RingBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Push(size_t amount) {
|
bool Push(size_t amount) {
|
||||||
InterruptLock lock;
|
|
||||||
|
|
||||||
if (amount > FreeSpace()) {
|
if (amount > FreeSpace()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -60,8 +52,6 @@ struct RingBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Pop(size_t amount) {
|
bool Pop(size_t amount) {
|
||||||
InterruptLock lock;
|
|
||||||
|
|
||||||
if (amount > AvailableData()) {
|
if (amount > AvailableData()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -72,15 +62,9 @@ struct RingBuffer {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t FreeSpace() const {
|
size_t FreeSpace() const { return buffer.size() - AvailableData(); }
|
||||||
InterruptLock lock;
|
|
||||||
|
|
||||||
return buffer.size() - AvailableData();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t AvailableData() const {
|
size_t AvailableData() const {
|
||||||
InterruptLock lock;
|
|
||||||
|
|
||||||
if (read_ptr == write_ptr) {
|
if (read_ptr == write_ptr) {
|
||||||
return full ? buffer.size() : 0;
|
return full ? buffer.size() : 0;
|
||||||
}
|
}
|
||||||
@ -88,14 +72,10 @@ struct RingBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* RawReadPointer() const {
|
uint8_t* RawReadPointer() const {
|
||||||
InterruptLock lock;
|
|
||||||
|
|
||||||
return reinterpret_cast<uint8_t*>(buffer.data() + read_ptr);
|
return reinterpret_cast<uint8_t*>(buffer.data() + read_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ContiguousAvailableData() const {
|
size_t ContiguousAvailableData() const {
|
||||||
InterruptLock lock;
|
|
||||||
|
|
||||||
if (read_ptr < write_ptr) {
|
if (read_ptr < write_ptr) {
|
||||||
return AvailableData();
|
return AvailableData();
|
||||||
}
|
}
|
||||||
|
@ -7,19 +7,14 @@
|
|||||||
#include "timer.h"
|
#include "timer.h"
|
||||||
#include "uart.h"
|
#include "uart.h"
|
||||||
|
|
||||||
#ifndef SBRK_STATS
|
|
||||||
#define SBRK_STATS 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern unsigned char _heap_begin, _heap_end;
|
extern unsigned char _heap_begin, _heap_end;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
[[maybe_unused]]
|
|
||||||
void LogStats(unsigned char* heap) {
|
void LogStats(unsigned char* heap) {
|
||||||
char number[] = "00000000\r\n";
|
char number[] = "00000000\r\n";
|
||||||
UartWriteCrash("Program break now at 0x");
|
UartWriteBlocking("Program break now at 0x");
|
||||||
itoa(reinterpret_cast<int>(heap), number);
|
itoa(reinterpret_cast<int>(heap), number);
|
||||||
UartWriteCrash(number);
|
UartWriteBlocking(number);
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@ -27,13 +22,11 @@ extern "C" void* _sbrk(int increment) {
|
|||||||
static unsigned char* heap = &_heap_begin;
|
static unsigned char* heap = &_heap_begin;
|
||||||
unsigned char* prev_heap = heap;
|
unsigned char* prev_heap = heap;
|
||||||
if (heap + increment >= &_heap_end) {
|
if (heap + increment >= &_heap_end) {
|
||||||
UartWriteCrash("Heap overflow!\r\n");
|
UartWriteBlocking("Heap overflow!\r\n");
|
||||||
return reinterpret_cast<void*>(-1);
|
return reinterpret_cast<void*>(-1);
|
||||||
}
|
}
|
||||||
heap += increment;
|
heap += increment;
|
||||||
#if SBRK_STATS
|
|
||||||
LogStats(heap);
|
LogStats(heap);
|
||||||
#endif
|
|
||||||
return prev_heap;
|
return prev_heap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ async::task<> UartWrite(std::span<const std::byte> data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define GCC_HAS_BUG_101133 0 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101133
|
#define GCC_HAS_BUG_101133 1 // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=101133
|
||||||
#if !GCC_HAS_BUG_101133
|
#if !GCC_HAS_BUG_101133
|
||||||
async::task<> UartWriteLoop(async::gimme<std::span<const std::byte>>& data_gen) {
|
async::task<> UartWriteLoop(async::gimme<std::span<const std::byte>>& data_gen) {
|
||||||
while (1) {
|
while (1) {
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
#include "uart.h"
|
|
||||||
#include "uart_async.h"
|
|
||||||
#include "fake_uart.h"
|
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <chrono>
|
|
||||||
#include <semaphore>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
using namespace ::testing;
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
std::atomic<bool> terminate;
|
|
||||||
std::counting_semaphore<12345> got_stuff(0);
|
|
||||||
|
|
||||||
buffer rx;
|
|
||||||
|
|
||||||
using namespace std::literals::chrono_literals;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string drain_n(size_t n, std::chrono::system_clock::duration timeout) {
|
|
||||||
std::string txtout;
|
|
||||||
auto begin = std::chrono::system_clock::now();
|
|
||||||
|
|
||||||
while (std::chrono::system_clock::now() < begin + timeout) {
|
|
||||||
auto out = FakeUart_Drain(n);
|
|
||||||
if (out.data.empty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto strout = std::string_view(reinterpret_cast<char*>(out.data.data()),
|
|
||||||
out.data.size());
|
|
||||||
n -= out.data.size();
|
|
||||||
txtout.append(strout);
|
|
||||||
if (n < 1) {
|
|
||||||
return txtout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Uart, BasicSend) {
|
|
||||||
FakeUart_Reset();
|
|
||||||
terminate = false;
|
|
||||||
|
|
||||||
constexpr std::string_view kTestData = "blarg";
|
|
||||||
auto buff = std::as_bytes(std::span{kTestData});
|
|
||||||
|
|
||||||
async::schedule(UartWrite(buff).h);
|
|
||||||
std::thread t(
|
|
||||||
[]() { async::main_loop([]() -> bool { return terminate; }); });
|
|
||||||
|
|
||||||
std::string strout = drain_n(kTestData.size(), 1s);
|
|
||||||
EXPECT_EQ(strout, kTestData);
|
|
||||||
|
|
||||||
terminate = true;
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
async::task<> test_echo() {
|
|
||||||
async::task<uint8_t> reader = UartReadLoop();
|
|
||||||
async::gimme<std::span<const std::byte>> feeder;
|
|
||||||
async::task<> writer = UartWriteLoop(feeder);
|
|
||||||
writer.h.resume(); // advance to first yield
|
|
||||||
while (1) {
|
|
||||||
uint8_t c = co_await reader;
|
|
||||||
feeder.feed(std::as_bytes(std::span{&c, 1}));
|
|
||||||
got_stuff.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(Uart, Echo) {
|
|
||||||
FakeUart_Reset();
|
|
||||||
terminate = false;
|
|
||||||
|
|
||||||
constexpr std::string_view kTestData = "blargblaektrkblalasrjkh1!!";
|
|
||||||
auto buff = std::as_bytes(std::span{kTestData});
|
|
||||||
|
|
||||||
async::schedule(test_echo().h);
|
|
||||||
std::thread t(
|
|
||||||
[]() { async::main_loop([]() -> bool { return terminate; }); });
|
|
||||||
|
|
||||||
for (int j = 0; j < 100; j++) {
|
|
||||||
FakeUart_Feed(buff);
|
|
||||||
std::string txtout = drain_n(kTestData.size(), 1s);
|
|
||||||
|
|
||||||
ASSERT_EQ(txtout, kTestData);
|
|
||||||
}
|
|
||||||
|
|
||||||
terminate = true;
|
|
||||||
t.join();
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user