//
|
////////////////////////////////////////////////////////////////////////////////
|
//
|
//
|
// Filename: pipecmdr.h
|
// Filename: pipecmdr.h
|
//
|
//
|
// Project: FPGA testbench utilities.
|
// Project: XuLA2-LX25 SoC based upon the ZipCPU
|
//
|
//
|
// Purpose: This program attaches to a Verilated Verilog IP core.
|
// Purpose: This program attaches to a Verilated Verilog IP core.
|
// It will not work apart from such a core. Once attached,
|
// It will not work apart from such a core. Once attached,
|
// it connects the simulated core to a controller via a
|
// it connects the simulated core to a controller via a pipe interface
|
// pipe interface designed to act like a UART. Indeed, it
|
// designed to act like a UART. Indeed, it is hoped that the final
|
// is hoped that the final interface would be via UART. Until
|
// interface would be via UART. Until that point, however, this is just
|
// that point, however, this is just a simple test facility
|
// a simple test facility designed to verify that the IP core works
|
// designed to verify that the IP core works prior to such
|
// prior to such actual hardware implementation.
|
// actual hardware implementation.
|
//
|
|
// Creator: Dan Gisselquist, Ph.D.
|
|
// Gisselquist Technology, LLC
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Copyright (C) 2015-2016, Gisselquist Technology, LLC
|
|
//
|
|
// This program is free software (firmware): you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License as published
|
|
// by the Free Software Foundation, either version 3 of the License, or (at
|
|
// your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
// for more details.
|
//
|
//
|
// Creator: Dan Gisselquist
|
// You should have received a copy of the GNU General Public License along
|
// Gisselquist Tecnology, LLC
|
// with this program. (It's in the $(ROOT)/doc directory, run make with no
|
|
// target there if the PDF file isn't present.) If not, see
|
|
// <http://www.gnu.org/licenses/> for a copy.
|
//
|
//
|
// Copyright: 2015
|
// License: GPL, v3, as defined and found on www.gnu.org,
|
|
// http://www.gnu.org/licenses/gpl.html
|
|
//
|
|
//
|
|
////////////////////////////////////////////////////////////////////////////////
|
//
|
//
|
//
|
//
|
#ifndef PIPECMDR_H
|
#ifndef PIPECMDR_H
|
#define PIPECMDR_H
|
#define PIPECMDR_H
|
|
|
#include <sys/types.h>
|
#include <sys/types.h>
|
#include <sys/socket.h>
|
#include <sys/socket.h>
|
#include <poll.h>
|
#include <poll.h>
|
#include <unistd.h>
|
#include <unistd.h>
|
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
|
|
#include "testb.h"
|
#include "testb.h"
|
|
|
#define PIPEBUFLEN 256
|
#define PIPEBUFLEN 256
|
|
|
// At 115200 Baud, 8 bits of data, no parity and one stop bit, there will
|
// At 115200 Baud, 8 bits of data, no parity and one stop bit, there will
|
// bit ten bits per character and therefore 8681 clocks per transfer
|
// bit ten bits per character and therefore 8681 clocks per transfer
|
// 8681 ~= 100 MHz / 115200 (bauds / second) * 10 bauds / character
|
// 8681 ~= 100 MHz / 115200 (bauds / second) * 10 bauds / character
|
//
|
//
|
// #define UARTLEN 8681 // Minimum ticks per character, 115200 Baud
|
// #define UARTLEN 8681 // Minimum ticks per character, 115200 Baud
|
//
|
//
|
// At 4MBaud, each bit takes 25 clocks. 10 bits would thus take 250 clocks
|
// At 4MBaud, each bit takes 25 clocks. 10 bits would thus take 250 clocks
|
//
|
//
|
// #define UARTLEN 250 // Minimum ticks per character, 4M Baud
|
// #define UARTLEN 250 // Minimum ticks per character, 4M Baud
|
// #define UARTLEN 1000 // Minimum ticks per character, 1M Hz
|
// #define UARTLEN 1000 // Minimum ticks per character, 1M Hz
|
// #define UARTLEN 8 // Minimum ticks per character
|
// #define UARTLEN 8 // Minimum ticks per character
|
#define UARTLEN 4096 //
|
#define UARTLEN 4096 //
|
|
|
template <class VA> class PIPECMDR : public TESTB<VA> {
|
template <class VA> class PIPECMDR : public TESTB<VA> {
|
void setup_listener(const int port) {
|
void setup_listener(const int port) {
|
struct sockaddr_in my_addr;
|
struct sockaddr_in my_addr;
|
|
|
printf("Listening on port %d\n", port);
|
printf("Listening on port %d\n", port);
|
|
|
m_skt = socket(AF_INET, SOCK_STREAM, 0);
|
m_skt = socket(AF_INET, SOCK_STREAM, 0);
|
if (m_skt < 0) {
|
if (m_skt < 0) {
|
perror("Could not allocate socket: ");
|
perror("Could not allocate socket: ");
|
exit(-1);
|
exit(-1);
|
}
|
}
|
|
|
// Set the reuse address option
|
// Set the reuse address option
|
{
|
{
|
int optv = 1, er;
|
int optv = 1, er;
|
er = setsockopt(m_skt, SOL_SOCKET, SO_REUSEADDR, &optv, sizeof(optv));
|
er = setsockopt(m_skt, SOL_SOCKET, SO_REUSEADDR, &optv, sizeof(optv));
|
if (er != 0) {
|
if (er != 0) {
|
perror("SockOpt Err:");
|
perror("SockOpt Err:");
|
exit(-1);
|
exit(-1);
|
}
|
}
|
}
|
}
|
|
|
memset(&my_addr, 0, sizeof(struct sockaddr_in)); // clear structure
|
memset(&my_addr, 0, sizeof(struct sockaddr_in)); // clear structure
|
my_addr.sin_family = AF_INET;
|
my_addr.sin_family = AF_INET;
|
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
my_addr.sin_port = htons(port);
|
my_addr.sin_port = htons(port);
|
|
|
if (bind(m_skt, (struct sockaddr *)&my_addr, sizeof(my_addr))!=0) {
|
if (bind(m_skt, (struct sockaddr *)&my_addr, sizeof(my_addr))!=0) {
|
perror("BIND FAILED:");
|
perror("BIND FAILED:");
|
exit(-1);
|
exit(-1);
|
}
|
}
|
|
|
if (listen(m_skt, 1) != 0) {
|
if (listen(m_skt, 1) != 0) {
|
perror("Listen failed:");
|
perror("Listen failed:");
|
exit(-1);
|
exit(-1);
|
}
|
}
|
}
|
}
|
|
|
public:
|
public:
|
int m_skt, m_con;
|
int m_skt, m_con;
|
char m_txbuf[PIPEBUFLEN], m_rxbuf[PIPEBUFLEN];
|
char m_txbuf[PIPEBUFLEN], m_rxbuf[PIPEBUFLEN];
|
int m_ilen, m_rxpos, m_txpos, m_uart_wait, m_tx_busy;
|
int m_ilen, m_rxpos, m_txpos, m_uart_wait, m_tx_busy;
|
bool m_started_flag;
|
bool m_started_flag;
|
|
|
PIPECMDR(const int port) : TESTB<VA>() {
|
PIPECMDR(const int port) : TESTB<VA>() {
|
m_con = m_skt = -1;
|
m_con = m_skt = -1;
|
setup_listener(port);
|
setup_listener(port);
|
m_rxpos = m_txpos = m_ilen = 0;
|
m_rxpos = m_txpos = m_ilen = 0;
|
m_started_flag = false;
|
m_started_flag = false;
|
m_uart_wait = 0; // Flow control into the FPGA
|
m_uart_wait = 0; // Flow control into the FPGA
|
m_tx_busy = 0; // Flow control out of the FPGA
|
m_tx_busy = 0; // Flow control out of the FPGA
|
}
|
}
|
|
|
virtual void kill(void) {
|
virtual void kill(void) {
|
// Close any active connection
|
// Close any active connection
|
if (m_con >= 0) close(m_con);
|
if (m_con >= 0) close(m_con);
|
if (m_skt >= 0) close(m_skt);
|
if (m_skt >= 0) close(m_skt);
|
}
|
}
|
|
|
virtual void tick(void) {
|
virtual void tick(void) {
|
if (m_con < 0) {
|
if (m_con < 0) {
|
// Can we accept a connection?
|
// Can we accept a connection?
|
struct pollfd pb;
|
struct pollfd pb;
|
|
|
pb.fd = m_skt;
|
pb.fd = m_skt;
|
pb.events = POLLIN;
|
pb.events = POLLIN;
|
poll(&pb, 1, 0);
|
poll(&pb, 1, 0);
|
|
|
if (pb.revents & POLLIN) {
|
if (pb.revents & POLLIN) {
|
m_con = accept(m_skt, 0, 0);
|
m_con = accept(m_skt, 0, 0);
|
|
|
if (m_con < 0)
|
if (m_con < 0)
|
perror("Accept failed:");
|
perror("Accept failed:");
|
}
|
}
|
}
|
}
|
|
|
TESTB<VA>::m_core->i_rx_stb = 0;
|
TESTB<VA>::m_core->i_rx_stb = 0;
|
|
|
if (m_uart_wait == 0) {
|
if (m_uart_wait == 0) {
|
if (m_ilen > 0) {
|
if (m_ilen > 0) {
|
// Is there a byte in our buffer somewhere?
|
// Is there a byte in our buffer somewhere?
|
TESTB<VA>::m_core->i_rx_stb = 1;
|
TESTB<VA>::m_core->i_rx_stb = 1;
|
TESTB<VA>::m_core->i_rx_data = m_rxbuf[m_rxpos++];
|
TESTB<VA>::m_core->i_rx_data = m_rxbuf[m_rxpos++];
|
m_ilen--;
|
m_ilen--;
|
} else if (m_con > 0) {
|
} else if (m_con > 0) {
|
// Is there a byte to be read here?
|
// Is there a byte to be read here?
|
struct pollfd pb;
|
struct pollfd pb;
|
pb.fd = m_con;
|
pb.fd = m_con;
|
pb.events = POLLIN;
|
pb.events = POLLIN;
|
if (poll(&pb, 1, 0) < 0)
|
if (poll(&pb, 1, 0) < 0)
|
perror("Polling error:");
|
perror("Polling error:");
|
if (pb.revents & POLLIN) {
|
if (pb.revents & POLLIN) {
|
if ((m_ilen =recv(m_con, m_rxbuf, sizeof(m_rxbuf), MSG_DONTWAIT)) > 0) {
|
if ((m_ilen =recv(m_con, m_rxbuf, sizeof(m_rxbuf), MSG_DONTWAIT)) > 0) {
|
m_rxbuf[m_ilen] = '\0';
|
m_rxbuf[m_ilen] = '\0';
|
if (m_rxbuf[m_ilen-1] == '\n') {
|
if (m_rxbuf[m_ilen-1] == '\n') {
|
m_rxbuf[m_ilen-1] = '\0';
|
m_rxbuf[m_ilen-1] = '\0';
|
printf("< \'%s\'\n", m_rxbuf);
|
printf("< \'%s\'\n", m_rxbuf);
|
m_rxbuf[m_ilen-1] = '\n';
|
m_rxbuf[m_ilen-1] = '\n';
|
} else printf("< \'%s\'\n", m_rxbuf);
|
} else printf("< \'%s\'\n", m_rxbuf);
|
TESTB<VA>::m_core->i_rx_stb = 1;
|
TESTB<VA>::m_core->i_rx_stb = 1;
|
TESTB<VA>::m_core->i_rx_data = m_rxbuf[0];
|
TESTB<VA>::m_core->i_rx_data = m_rxbuf[0];
|
m_rxpos = 1; m_ilen--;
|
m_rxpos = 1; m_ilen--;
|
m_started_flag = true;
|
m_started_flag = true;
|
} else if (m_ilen < 0) {
|
} else if (m_ilen < 0) {
|
// An error occurred, close the connection
|
// An error occurred, close the connection
|
// This could also be the
|
// This could also be the
|
// indication of a simple
|
// indication of a simple
|
// connection close, so we deal
|
// connection close, so we deal
|
// with this quietly.
|
// with this quietly.
|
// perror("Read error: ");
|
// perror("Read error: ");
|
// fprintf(stderr, "Closing connection\n");
|
// fprintf(stderr, "Closing connection\n");
|
close(m_con);
|
close(m_con);
|
m_con = -1;
|
m_con = -1;
|
} else { // the connection closed on us
|
} else { // the connection closed on us
|
close(m_con);
|
close(m_con);
|
m_con = -1;
|
m_con = -1;
|
}
|
}
|
}
|
}
|
} m_uart_wait = (TESTB<VA>::m_core->i_rx_stb)?UARTLEN:0;
|
} m_uart_wait = (TESTB<VA>::m_core->i_rx_stb)?UARTLEN:0;
|
} else {
|
} else {
|
// Still working on transmitting a character
|
// Still working on transmitting a character
|
m_uart_wait = m_uart_wait - 1;
|
m_uart_wait = m_uart_wait - 1;
|
}
|
}
|
|
|
/*
|
/*
|
if (TESTB<VA>::m_core->i_rx_stb) {
|
if (TESTB<VA>::m_core->i_rx_stb) {
|
putchar(TESTB<VA>::m_core->i_rx_data);
|
putchar(TESTB<VA>::m_core->i_rx_data);
|
fflush(stdout);
|
fflush(stdout);
|
}
|
}
|
*/
|
*/
|
TESTB<VA>::tick();
|
TESTB<VA>::tick();
|
|
|
bool tx_accepted = false;
|
bool tx_accepted = false;
|
if (m_tx_busy == 0) {
|
if (m_tx_busy == 0) {
|
if ((TESTB<VA>::m_core->o_tx_stb)&&(m_con > 0)) {
|
if ((TESTB<VA>::m_core->o_tx_stb)&&(m_con > 0)) {
|
m_txbuf[m_txpos++] = TESTB<VA>::m_core->o_tx_data;
|
m_txbuf[m_txpos++] = TESTB<VA>::m_core->o_tx_data;
|
tx_accepted = true;
|
tx_accepted = true;
|
if ((TESTB<VA>::m_core->o_tx_data == '\n')||(m_txpos >= sizeof(m_txbuf))) {
|
if ((TESTB<VA>::m_core->o_tx_data == '\n')||(m_txpos >= sizeof(m_txbuf))) {
|
int snt = 0;
|
int snt = 0;
|
snt = send(m_con, m_txbuf, m_txpos, 0);
|
snt = send(m_con, m_txbuf, m_txpos, 0);
|
|
if (snt < 0) {
|
|
close(m_con);
|
|
m_con = -1;
|
|
snt = 0;
|
|
}
|
m_txbuf[m_txpos] = '\0';
|
m_txbuf[m_txpos] = '\0';
|
printf("> %s", m_txbuf);
|
printf("> %s", m_txbuf);
|
if (snt < m_txpos) {
|
if (snt < m_txpos) {
|
fprintf(stderr, "Only sent %d bytes!\n",
|
fprintf(stderr, "Only sent %d bytes of %d!\n",
|
snt);
|
snt, m_txpos);
|
}
|
}
|
m_txpos = 0;
|
m_txpos = 0;
|
}
|
}
|
}
|
}
|
} else
|
} else
|
m_tx_busy--;
|
m_tx_busy--;
|
|
|
if ((TESTB<VA>::m_core->o_tx_stb)&&(TESTB<VA>::m_core->i_tx_busy==0))
|
if ((TESTB<VA>::m_core->o_tx_stb)&&(TESTB<VA>::m_core->i_tx_busy==0))
|
m_tx_busy = UARTLEN;
|
m_tx_busy = UARTLEN;
|
TESTB<VA>::m_core->i_tx_busy = (m_tx_busy != 0);
|
TESTB<VA>::m_core->i_tx_busy = (m_tx_busy != 0);
|
|
|
if (0) {
|
if (0) {
|
if ((m_tx_busy!=0)||(TESTB<VA>::m_core->i_tx_busy)
|
if ((m_tx_busy!=0)||(TESTB<VA>::m_core->i_tx_busy)
|
||(TESTB<VA>::m_core->o_tx_stb)
|
||(TESTB<VA>::m_core->o_tx_stb)
|
||(tx_accepted))
|
||(tx_accepted))
|
printf("%4d %d %d %02x %s\n",
|
printf("%4d %d %d %02x %s\n",
|
m_tx_busy,
|
m_tx_busy,
|
TESTB<VA>::m_core->i_tx_busy,
|
TESTB<VA>::m_core->i_tx_busy,
|
TESTB<VA>::m_core->o_tx_stb,
|
TESTB<VA>::m_core->o_tx_stb,
|
TESTB<VA>::m_core->o_tx_data,
|
TESTB<VA>::m_core->o_tx_data,
|
(tx_accepted)?"READ!":"");
|
(tx_accepted)?"READ!":"");
|
}
|
}
|
|
|
|
|
/*
|
/*
|
if((TESTB<VA>::m_core->o_wb_cyc)||(TESTB<VA>::m_core->o_wb_stb)){
|
if((TESTB<VA>::m_core->o_wb_cyc)||(TESTB<VA>::m_core->o_wb_stb)){
|
printf("BUS: %d,%d,%d %8x %8x\n",
|
printf("BUS: %d,%d,%d %8x %8x\n",
|
TESTB<VA>::m_core->o_wb_cyc,
|
TESTB<VA>::m_core->o_wb_cyc,
|
TESTB<VA>::m_core->o_wb_stb,
|
TESTB<VA>::m_core->o_wb_stb,
|
TESTB<VA>::m_core->o_wb_we,
|
TESTB<VA>::m_core->o_wb_we,
|
TESTB<VA>::m_core->o_wb_addr,
|
TESTB<VA>::m_core->o_wb_addr,
|
TESTB<VA>::m_core->o_wb_data);
|
TESTB<VA>::m_core->o_wb_data);
|
} else if (m_started_flag) {
|
} else if (m_started_flag) {
|
printf("%02x,%c,%d,%d,%02x -> %d,%d,%d, %2x,%2x,%2x\n",
|
printf("%02x,%c,%d,%d,%02x -> %d,%d,%d, %2x,%2x,%2x\n",
|
TESTB<VA>::m_core->i_rx_data,
|
TESTB<VA>::m_core->i_rx_data,
|
(TESTB<VA>::m_core->i_rx_stb)?(TESTB<VA>::m_core->i_rx_data):' ',
|
(TESTB<VA>::m_core->i_rx_stb)?(TESTB<VA>::m_core->i_rx_data):' ',
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__r_valid,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__r_valid,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__rx_eol,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__rx_eol,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__rx_six_bits,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__rx_six_bits,
|
//
|
//
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__o_rq_strobe,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__o_rq_strobe,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__o_rq_hold,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__o_rq_hold,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__o_rq_we,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__o_rq_we,
|
//
|
//
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__state,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__state,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__nreg,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__nreg,
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__szreg);
|
TESTB<VA>::m_core->v__DOT__decodewb__DOT__szreg);
|
}
|
}
|
*/
|
*/
|
}
|
}
|
};
|
};
|
|
|
#endif
|
#endif
|
|
|