1 // Copyright 2017-2019 VMware, Inc.
2 // SPDX-License-Identifier: BSD-2-Clause
3 //
4 // The BSD-2 license (the License) set forth below applies to all parts of the
5 // Cascade project. You may not use this file except in compliance with the
6 // License.
7 //
8 // BSD-2 License
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are met:
12 //
13 // 1. Redistributions of source code must retain the above copyright notice, this
14 // list of conditions and the following disclaimer.
15 //
16 // 2. Redistributions in binary form must reproduce the above copyright notice,
17 // this list of conditions and the following disclaimer in the documentation
18 // and/or other materials provided with the distribution.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND
21 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
31 #include "target/core/avmm/de10/de10_compiler.h"
32
33 #include <cstdlib>
34 #include <fcntl.h>
35 #include <fstream>
36 #include <sys/mman.h>
37 #include "common/sockstream.h"
38 #include "common/system.h"
39 #include "target/core/avmm/de10/de10_config.h"
40 #include "target/core/avmm/de10/quartus_server.h"
41
42 using namespace std;
43
44 namespace cascade::avmm {
45
46 constexpr auto HW_REGS_BASE = 0xfc000000u;
47 constexpr auto HW_REGS_SPAN = 0x04000000u;
48 constexpr auto HW_REGS_MASK = HW_REGS_SPAN - 1;
49 constexpr auto ALT_LWFPGALVS_OFST = 0xff200000u;
50 constexpr auto LED_PIO_BASE = 0x00003000u;
51 constexpr auto PAD_PIO_BASE = 0x00004000u;
52 constexpr auto GPIO_PIO_BASE = 0x00005000u;
53 constexpr auto LOG_PIO_BASE = 0x00040000u;
54
De10Compiler()55 De10Compiler::De10Compiler() : AvmmCompiler<2,12,uint16_t,uint32_t>() {
56 fd_ = open("/dev/mem", (O_RDWR | O_SYNC));
57 if (fd_ == -1) {
58 virtual_base_ = reinterpret_cast<volatile uint8_t*>(MAP_FAILED);
59 } else {
60 virtual_base_ = reinterpret_cast<volatile uint8_t*>(mmap(nullptr, HW_REGS_SPAN, (PROT_READ|PROT_WRITE), MAP_SHARED, fd_, HW_REGS_BASE));
61 }
62
63 set_host("localhost");
64 set_port(9900);
65 }
66
~De10Compiler()67 De10Compiler::~De10Compiler() {
68 // Close the descriptor, and unmap the memory associated with the fpga
69 if (fd_ != -1) {
70 close(fd_);
71 }
72 if (virtual_base_ != MAP_FAILED) {
73 munmap(reinterpret_cast<void*>(const_cast<uint8_t*>(virtual_base_)), HW_REGS_SPAN);
74 }
75 }
76
set_host(const string & host)77 De10Compiler& De10Compiler::set_host(const string& host) {
78 host_ = host;
79 return *this;
80 }
81
set_port(uint32_t port)82 De10Compiler& De10Compiler::set_port(uint32_t port) {
83 port_ = port;
84 return *this;
85 }
86
build(Interface * interface,ModuleDeclaration * md,size_t slot)87 De10Logic* De10Compiler::build(Interface* interface, ModuleDeclaration* md, size_t slot) {
88 volatile uint8_t* addr = virtual_base_+((ALT_LWFPGALVS_OFST + LOG_PIO_BASE) & HW_REGS_MASK) + (slot << 14);
89 return new De10Logic(interface, md, slot, addr);
90 }
91
compile(const string & text,mutex & lock)92 bool De10Compiler::compile(const string& text, mutex& lock) {
93 sockstream sock(host_.c_str(), port_);
94 assert(!sock.error());
95
96 compile(&sock, text);
97 lock.unlock();
98 const auto res = block_on_compile(&sock);
99 lock.lock();
100
101 if (res) {
102 reprogram(&sock);
103 return true;
104 } else {
105 return false;
106 }
107 }
108
stop_compile()109 void De10Compiler::stop_compile() {
110 sockstream sock(host_.c_str(), port_);
111 assert(!sock.error());
112
113 sock.put(static_cast<uint8_t>(QuartusServer::Rpc::KILL_ALL));
114 sock.flush();
115 sock.get();
116 }
117
compile_gpio(Engine::Id id,ModuleDeclaration * md,Interface * interface)118 De10Gpio* De10Compiler::compile_gpio(Engine::Id id, ModuleDeclaration* md, Interface* interface) {
119 (void) id;
120
121 if (virtual_base_ == MAP_FAILED) {
122 get_compiler()->error("De10 gpio compilation failed due to inability to memory map device");
123 delete md;
124 return nullptr;
125 }
126 if (!check_io(md, 8, 8)) {
127 get_compiler()->error("Unable to compile a de10 gpio with more than 8 outputs");
128 delete md;
129 return nullptr;
130 }
131
132 volatile uint8_t* led_addr = virtual_base_+((ALT_LWFPGALVS_OFST + GPIO_PIO_BASE) & HW_REGS_MASK);
133 if (!ModuleInfo(md).inputs().empty()) {
134 const auto* in = *ModuleInfo(md).inputs().begin();
135 const auto iid = to_vid(in);
136 delete md;
137 return new De10Gpio(interface, iid, led_addr);
138 } else {
139 delete md;
140 return new De10Gpio(interface, nullid(), led_addr);
141 }
142 }
143
compile_led(Engine::Id id,ModuleDeclaration * md,Interface * interface)144 De10Led* De10Compiler::compile_led(Engine::Id id, ModuleDeclaration* md, Interface* interface) {
145 (void) id;
146
147 if (virtual_base_ == MAP_FAILED) {
148 get_compiler()->error("De10 led compilation failed due to inability to memory map device");
149 delete md;
150 return nullptr;
151 }
152 if (!check_io(md, 8, 8)) {
153 get_compiler()->error("Unable to compile a de10 led with more than 8 outputs");
154 delete md;
155 return nullptr;
156 }
157
158 volatile uint8_t* led_addr = virtual_base_+((ALT_LWFPGALVS_OFST + LED_PIO_BASE) & HW_REGS_MASK);
159 if (!ModuleInfo(md).inputs().empty()) {
160 const auto* in = *ModuleInfo(md).inputs().begin();
161 const auto iid = to_vid(in);
162 delete md;
163 return new De10Led(interface, iid, led_addr);
164 } else {
165 delete md;
166 return new De10Led(interface, nullid(), led_addr);
167 }
168 }
169
compile_pad(Engine::Id id,ModuleDeclaration * md,Interface * interface)170 De10Pad* De10Compiler::compile_pad(Engine::Id id, ModuleDeclaration* md, Interface* interface) {
171 (void) id;
172
173 if (virtual_base_ == MAP_FAILED) {
174 get_compiler()->error("De10 pad compilation failed due to inability to memory map device");
175 delete md;
176 return nullptr;
177 }
178 if (!check_io(md, 0, 4)) {
179 get_compiler()->error("Unable to compile a de10 pad with more than 4 inputs");
180 delete md;
181 return nullptr;
182 }
183
184 volatile uint8_t* pad_addr = virtual_base_+((ALT_LWFPGALVS_OFST + PAD_PIO_BASE) & HW_REGS_MASK);
185 const auto* out = *ModuleInfo(md).outputs().begin();
186 const auto oid = to_vid(out);
187 const auto w = Evaluate().get_width(out);
188 delete md;
189
190 return new De10Pad(interface, oid, w, pad_addr);
191 }
192
compile(sockstream * sock,const string & text)193 void De10Compiler::compile(sockstream* sock, const string& text) {
194 // Send a compile request. We'll block here until there are no more compile threads
195 // running.
196 sock->put(static_cast<uint8_t>(QuartusServer::Rpc::COMPILE));
197 sock->flush();
198 sock->get();
199
200 // Send code to the quartus server, we won't hear back from this socket until
201 // compilation is finished or it was aborted.
202 sock->write(text.c_str(), text.length());
203 sock->put('\0');
204 sock->flush();
205 }
206
block_on_compile(sockstream * sock)207 bool De10Compiler::block_on_compile(sockstream* sock) {
208 return (static_cast<QuartusServer::Rpc>(sock->get()) == QuartusServer::Rpc::OKAY);
209 }
210
reprogram(sockstream * sock)211 void De10Compiler::reprogram(sockstream* sock) {
212 get_compiler()->schedule_state_safe_interrupt([this, sock]{
213 // Send REPROGRAM request
214 sock->put(static_cast<uint8_t>(QuartusServer::Rpc::REPROGRAM));
215 sock->flush();
216
217 // We'll receive a 32-bit value indicating number of bytes and then the contents
218 // of the rbf file that was generated by the quartus server
219 uint32_t len = 0;
220 sock->read(reinterpret_cast<char*>(&len), sizeof(len));
221 vector<char> rbf;
222 rbf.resize(len);
223 sock->read(reinterpret_cast<char*>(rbf.data()), len);
224
225 // Form a path to the temporary location we'll be storing the rbf file
226 System::execute("mkdir -p /tmp/de10/");
227 char path[] = "/tmp/de10/DE10_NANO_SoC_GHRD_XXXXXX.rbf";
228 const auto fd = mkstemps(path, 4);
229 close(fd);
230
231 // Copy the rbf file to this location
232 ofstream ofs(path);
233 ofs.write(rbf.data(), len);
234 ofs.close();
235
236 // Run the reprogram tool
237 De10Config().run(path);
238
239 // Send a byte to acknowledge that we're done
240 sock->put(0);
241 sock->flush();
242 });
243 }
244
245 } // namespace cascade::avmm
246