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