| // Copyright 2024 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include <verilated.h> |
| |
| #include "VCoreMiniAxi.h" |
| #include "src/buses/axi-slave.h" |
| #include "src/buses/axi.h" |
| #include "src/buses/axilite.h" |
| #if VM_TRACE |
| #include <verilated_fst_c.h> |
| #endif |
| |
| #include <thread> |
| #include <signal.h> |
| |
| #include "tests/renode/kelvin.grpc.pb.h" |
| #include <grpcpp/grpcpp.h> |
| |
| RenodeAgent* kelvin_slave; |
| RenodeAgent* kelvin_master; |
| auto* axi_slave = new Axi(128, 32); |
| auto* axi_master = new AxiSlave(128, 32); |
| VCoreMiniAxi* top = new VCoreMiniAxi; |
| |
| uint8_t uint8_dummy; |
| VerilatedFstC* tfp; |
| vluint64_t main_time = 0; |
| vluint64_t last_tick = 0; |
| |
| |
| void tick() { |
| if (!top) { |
| return; |
| } |
| |
| // First cycle, always evaluate regardless of what role asked. |
| if (main_time == 0) { |
| top->io_irqn = true; |
| top->eval(); |
| main_time++; |
| return; |
| } |
| |
| // If we've already done this tick, skip. Otherwise, tick and update `last_tick`. |
| if (main_time == last_tick) { |
| return; |
| } else { |
| // On rising-edges, check if the core is in WFI. |
| // If so, generate an interrupt pulse to wake it. |
| static bool irqn_state = true; |
| if (top->io_aclk) { |
| if (top->io_wfi && irqn_state) { |
| irqn_state = false; |
| } |
| if (!top->io_wfi && !irqn_state) { |
| irqn_state = true; |
| } |
| } |
| top->io_irqn = irqn_state; |
| top->eval(); |
| last_tick = main_time; |
| main_time++; |
| } |
| |
| #if VM_TRACE |
| tfp->dump(main_time); |
| static bool flushed_on_halt = false; |
| if ((top->io_halted == 1 && !flushed_on_halt) || ((main_time % 1000) == 0)) { |
| flushed_on_halt = top->io_halted; |
| tfp->flush(); |
| } |
| #endif |
| } |
| |
| void axiSlaveEval() { |
| tick(); |
| if (kelvin_slave) { |
| kelvin_slave->handleInterrupts(); |
| } |
| } |
| |
| void axiMasterEval() { |
| tick(); |
| if (kelvin_master) { |
| kelvin_master->handleInterrupts(); |
| } |
| } |
| |
| RenodeAgent* Init(bool slave) { |
| if (slave) { |
| kelvin_slave = new RenodeAgent(axi_slave); |
| axi_slave->aclk = &top->io_aclk; |
| axi_slave->aresetn = &top->io_aresetn; |
| |
| axi_slave->awid = &top->io_axi_slave_write_addr_bits_id; |
| axi_slave->awaddr = &top->io_axi_slave_write_addr_bits_addr; |
| axi_slave->awlen = &top->io_axi_slave_write_addr_bits_len; |
| axi_slave->awsize = &top->io_axi_slave_write_addr_bits_size; |
| axi_slave->awburst = &top->io_axi_slave_write_addr_bits_burst; |
| axi_slave->awlock = &top->io_axi_slave_write_addr_bits_lock; |
| axi_slave->awcache = &top->io_axi_slave_write_addr_bits_cache; |
| axi_slave->awprot = &top->io_axi_slave_write_addr_bits_prot; |
| axi_slave->awqos = &top->io_axi_slave_write_addr_bits_qos; |
| axi_slave->awregion = &top->io_axi_slave_write_addr_bits_region; |
| axi_slave->awuser = &uint8_dummy; |
| axi_slave->awvalid = &top->io_axi_slave_write_addr_valid; |
| axi_slave->awready = &top->io_axi_slave_write_addr_ready; |
| |
| axi_slave->wdata = (uint32_t*)&top->io_axi_slave_write_data_bits_data; |
| axi_slave->wstrb = (uint8_t*)&top->io_axi_slave_write_data_bits_strb; |
| axi_slave->wlast = &top->io_axi_slave_write_data_bits_last; |
| axi_slave->wuser = &uint8_dummy; |
| axi_slave->wvalid = &top->io_axi_slave_write_data_valid; |
| axi_slave->wready = &top->io_axi_slave_write_data_ready; |
| |
| axi_slave->bid = &top->io_axi_slave_write_resp_bits_id; |
| axi_slave->bresp = &top->io_axi_slave_write_resp_bits_resp; |
| axi_slave->buser = &uint8_dummy; |
| axi_slave->bvalid = &top->io_axi_slave_write_resp_valid; |
| axi_slave->bready = &top->io_axi_slave_write_resp_ready; |
| |
| axi_slave->arid = &top->io_axi_slave_read_addr_bits_id; |
| axi_slave->araddr = &top->io_axi_slave_read_addr_bits_addr; |
| axi_slave->arlen = &top->io_axi_slave_read_addr_bits_len; |
| axi_slave->arsize = &top->io_axi_slave_read_addr_bits_size; |
| axi_slave->arburst = &top->io_axi_slave_read_addr_bits_burst; |
| axi_slave->arlock = &top->io_axi_slave_read_addr_bits_lock; |
| axi_slave->arcache = &top->io_axi_slave_read_addr_bits_cache; |
| axi_slave->arprot = &top->io_axi_slave_read_addr_bits_prot; |
| axi_slave->arqos = &top->io_axi_slave_read_addr_bits_qos; |
| axi_slave->arregion = &top->io_axi_slave_read_addr_bits_region; |
| axi_slave->aruser = &uint8_dummy; |
| axi_slave->arvalid = &top->io_axi_slave_read_addr_valid; |
| axi_slave->arready = &top->io_axi_slave_read_addr_ready; |
| |
| axi_slave->rid = &top->io_axi_slave_read_data_bits_id; |
| axi_slave->rdata = (uint32_t*)&top->io_axi_slave_read_data_bits_data; |
| axi_slave->rresp = &top->io_axi_slave_read_data_bits_resp; |
| axi_slave->rlast = &top->io_axi_slave_read_data_bits_last; |
| axi_slave->ruser = &uint8_dummy; |
| axi_slave->rvalid = &top->io_axi_slave_read_data_valid; |
| axi_slave->rready = &top->io_axi_slave_read_data_ready; |
| axi_slave->evaluateModel = &axiSlaveEval; |
| return kelvin_slave; |
| } else { |
| kelvin_master = new RenodeAgent(axi_master); |
| kelvin_slave->addBus(axi_master); |
| axi_master->aclk = &top->io_aclk; |
| axi_master->aresetn = &top->io_aresetn; |
| |
| axi_master->awid = &top->io_axi_master_write_addr_bits_id; |
| axi_master->awaddr = &top->io_axi_master_write_addr_bits_addr; |
| axi_master->awlen = &top->io_axi_master_write_addr_bits_len; |
| axi_master->awsize = &top->io_axi_master_write_addr_bits_size; |
| axi_master->awburst = &top->io_axi_master_write_addr_bits_burst; |
| axi_master->awlock = &top->io_axi_master_write_addr_bits_lock; |
| axi_master->awcache = &top->io_axi_master_write_addr_bits_cache; |
| axi_master->awprot = &top->io_axi_master_write_addr_bits_prot; |
| axi_master->awqos = &top->io_axi_master_write_addr_bits_qos; |
| axi_master->awregion = &top->io_axi_master_write_addr_bits_region; |
| axi_master->awuser = &uint8_dummy; |
| axi_master->awvalid = &top->io_axi_master_write_addr_valid; |
| axi_master->awready = &top->io_axi_master_write_addr_ready; |
| |
| axi_master->wdata = (uint32_t*)&top->io_axi_master_write_data_bits_data; |
| axi_master->wstrb = (uint8_t*)&top->io_axi_master_write_data_bits_strb; |
| axi_master->wlast = &top->io_axi_master_write_data_bits_last; |
| axi_master->wuser = &uint8_dummy; |
| axi_master->wvalid = &top->io_axi_master_write_data_valid; |
| axi_master->wready = &top->io_axi_master_write_data_ready; |
| |
| axi_master->bid = &top->io_axi_master_write_resp_bits_id; |
| axi_master->bresp = &top->io_axi_master_write_resp_bits_resp; |
| axi_master->buser = &uint8_dummy; |
| axi_master->bvalid = &top->io_axi_master_write_resp_valid; |
| axi_master->bready = &top->io_axi_master_write_resp_ready; |
| |
| axi_master->arid = &top->io_axi_master_read_addr_bits_id; |
| axi_master->araddr = &top->io_axi_master_read_addr_bits_addr; |
| axi_master->arlen = &top->io_axi_master_read_addr_bits_len; |
| axi_master->arsize = &top->io_axi_master_read_addr_bits_size; |
| axi_master->arburst = &top->io_axi_master_read_addr_bits_burst; |
| axi_master->arlock = &top->io_axi_master_read_addr_bits_lock; |
| axi_master->arcache = &top->io_axi_master_read_addr_bits_cache; |
| axi_master->arprot = &top->io_axi_master_read_addr_bits_prot; |
| axi_master->arqos = &top->io_axi_master_read_addr_bits_qos; |
| axi_master->arregion = &top->io_axi_master_read_addr_bits_region; |
| axi_master->aruser = &uint8_dummy; |
| axi_master->arvalid = &top->io_axi_master_read_addr_valid; |
| axi_master->arready = &top->io_axi_master_read_addr_ready; |
| |
| axi_master->rid = &top->io_axi_master_read_data_bits_id; |
| axi_master->rdata = (uint32_t*)&top->io_axi_master_read_data_bits_data; |
| axi_master->rresp = &top->io_axi_master_read_data_bits_resp; |
| axi_master->rlast = &top->io_axi_master_read_data_bits_last; |
| axi_master->ruser = &uint8_dummy; |
| axi_master->rvalid = &top->io_axi_master_read_data_valid; |
| axi_master->rready = &top->io_axi_master_read_data_ready; |
| axi_master->evaluateModel = &axiMasterEval; |
| return kelvin_master; |
| } |
| } |
| |
| // Stub to support linking with Renode's integration library. |
| // Only used for the shared library version, which we do not support. |
| RenodeAgent* Init() { |
| abort(); |
| return nullptr; |
| } |
| |
| std::thread kelvin_slave_thread; |
| std::thread kelvin_master_thread; |
| std::string kelvin_slave_address; |
| std::string kelvin_master_address; |
| |
| class KelvinServiceImpl final : public kelvin::Kelvin::Service { |
| grpc::Status StartAgent( |
| grpc::ServerContext* context, |
| const kelvin::StartAgentRequest* request, |
| kelvin::StartAgentResponse* response) override { |
| auto type = request->type(); |
| auto receiverPort = request->receiverport(); |
| auto senderPort = request->senderport(); |
| auto address = request->address().c_str(); |
| switch (type) { |
| case kelvin::AgentType::Slave: { |
| kelvin_slave_address = address; |
| Init(true); |
| kelvin_slave_thread = std::thread([=]() { |
| kelvin_slave->simulate(receiverPort, senderPort, kelvin_slave_address.c_str()); |
| }); |
| } |
| break; |
| case kelvin::AgentType::Master: { |
| kelvin_master_address = address; |
| Init(false); |
| kelvin_master_thread = std::thread([=]() { |
| kelvin_master->simulate(receiverPort, senderPort, kelvin_master_address.c_str()); |
| }); |
| } |
| break; |
| default: |
| break; |
| } |
| return grpc::Status::OK; |
| } |
| }; |
| |
| std::unique_ptr<grpc::Server> server; |
| // SIGTERM handler to shut down gRPC. |
| // NB: We spawn a new thread to call shutdown, as |
| // it must not be on the same thread as the server. |
| void sigterm_handler(int signo, siginfo_t* info, void* context) { |
| std::thread([]() { |
| server->Shutdown(); |
| }).join(); |
| } |
| |
| extern "C" int main(int argc, char** argv) { |
| #if VM_TRACE |
| Verilated::traceEverOn(true); |
| tfp = new VerilatedFstC; |
| top->trace(tfp, 99); |
| tfp->open("/tmp/core_mini_axi.fst"); |
| #endif |
| |
| // TODO(atv): How to fix this port? |
| std::string server_address = "127.0.0.1:9003"; |
| KelvinServiceImpl service; |
| |
| grpc::ServerBuilder builder; |
| builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); |
| builder.RegisterService(&service); |
| server = builder.BuildAndStart(); |
| |
| Verilated::commandArgs(argc, argv); |
| |
| // Setup a SIGTERM handler to shut down gRPC. |
| struct sigaction act = {0}; |
| act.sa_flags = SA_SIGINFO; |
| act.sa_sigaction = sigterm_handler; |
| sigaction(SIGTERM, &act, NULL); |
| |
| server->Wait(); |
| if (kelvin_master_thread.joinable()) |
| kelvin_master_thread.join(); |
| if (kelvin_slave_thread.joinable()) |
| kelvin_slave_thread.join(); |
| |
| top->final(); |
| #if VM_TRACE |
| if (tfp) { |
| tfp->close(); |
| tfp = nullptr; |
| } |
| #endif |
| |
| return 0; |
| } |