OpenCores
URL https://opencores.org/ocsvn/wbscope/wbscope/trunk

Subversion Repositories wbscope

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /wbscope
    from Rev 11 to Rev 12
    Reverse comparison

Rev 11 → Rev 12

/trunk/README.md
0,0 → 1,32
# A Wishbone Controlled Scope for FPGA's
 
This is a generic/library routine for providing a bus accessed 'scope' or
(perhaps more appropriately) a bus accessed logic analyzer for use internal to
an FPGA. The general operation is such that this 'scope' can record and report
on any 32 bit value transiting through the FPGA. Once started and reset, the
scope records a copy of the input data every time the clock ticks with the
circuit enabled. That is, it records these values up until the trigger. Once
the trigger goes high, the scope will record for ``bw_holdoff`` more counts
before stopping. Values may then be read from the buffer, oldest to most
recent. After reading, the scope may then be reset for another run.
 
In general, therefore, operation happens in this fashion:
 
1. A reset is issued.
2. Recording starts, in a circular buffer, and continues until
3. The trigger line is asserted.
The scope registers the asserted trigger by setting the ``o_triggered`` output flag.
4. A counter then ticks until the last value is written.
The scope registers that it has stopped recording by setting the ``o_stopped`` output flag.
5. The scope recording is then paused until the next reset.
6. While stopped, the CPU can read the data from the scope
 
- oldest to most recent
- one value per bus clock
7. Writes to the data register reset the address to the beginning of the buffer
 
# Commercial Applications
 
Should you find the GPLv3 license insufficient for your needs, other licenses
can be purchased from Gisselquist Technology, LLC.
/trunk/bench/cpp/Makefile
0,0 → 1,49
################################################################################
##
## Filename:
##
## Project: WBScope, a wishbone hosted scope
##
## Purpose:
##
## Creator: Dan Gisselquist, Ph.D.
## Gisselquist Technology, LLC
##
################################################################################
##
## Copyright (C) 2015-2017, 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.
##
## You should have received a copy of the GNU General Public License along
## 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.
##
## License: GPL, v3, as defined and found on www.gnu.org,
## http://www.gnu.org/licenses/gpl.html
##
##
################################################################################
##
##
all: wbscope_tb
CXX := g++
RTLD := ../rtl
ROBJD:= $(RTLD)/obj_dir
VROOT:= /usr/share/verilator
VINCS:= -I$(VROOT)
INCS := -I$(VROOT)/include -I$(ROBJD)
VSRCS:= $(VROOT)/include/verilated.cpp $(VROOT)/include/verilated_vcd_c.cpp
TBOBJ:= $(ROBJD)/Vwbscope_tb__ALL.a
 
wbscope_tb: wbscope_tb.cpp $(ROBJD)/Vwbscope_tb__ALL.a $(ROBJD)/Vwbscope_tb.h
$(CXX) $(INCS) wbscope_tb.cpp $(VSRCS) $(TBOBJ) -o $@
/trunk/bench/cpp/testb.h
0,0 → 1,104
////////////////////////////////////////////////////////////////////////////////
//
// Filename: testb.h
//
// Project: Zip CPU -- a small, lightweight, RISC CPU core
//
// Purpose: A wrapper for a common interface to a clocked FPGA core
// begin exercised in Verilator.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015,2017, 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.
//
// You should have received a copy of the GNU General Public License along
// 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.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
#ifndef TESTB_H
#define TESTB_H
 
#include <stdio.h>
#include <stdint.h>
#include <verilated_vcd_c.h>
 
template <class VA> class TESTB {
public:
VA *m_core;
VerilatedVcdC* m_trace;
unsigned long m_tickcount;
 
TESTB(void) : m_trace(NULL), m_tickcount(0l) {
m_core = new VA;
Verilated::traceEverOn(true);
}
virtual ~TESTB(void) {
if (m_trace) m_trace->close();
delete m_core;
m_core = NULL;
}
 
virtual void opentrace(const char *vcdname) {
m_trace = new VerilatedVcdC;
m_core->trace(m_trace, 99);
m_trace->open(vcdname);
}
 
virtual void closetrace(void) {
if (m_trace) {
m_trace->close();
m_trace = NULL;
}
}
 
virtual void eval(void) {
m_core->eval();
}
 
virtual void tick(void) {
m_tickcount++;
 
//if((m_trace)&&(m_tickcount)) m_trace->dump(10*m_tickcount-4);
eval();
if ((m_trace)&&(m_tickcount)) m_trace->dump(10*m_tickcount-2);
m_core->i_clk = 1;
eval();
if (m_trace) m_trace->dump(10*m_tickcount);
m_core->i_clk = 0;
eval();
if (m_trace) m_trace->dump(10*m_tickcount+5);
 
}
 
virtual void reset(void) {
m_core->i_rst = 1;
tick();
m_core->i_rst = 0;
// printf("RESET\n");
}
 
unsigned long tickcount(void) {
return m_tickcount;
}
};
 
#endif
/trunk/bench/cpp/wbscope_tb.cpp
0,0 → 1,282
////////////////////////////////////////////////////////////////////////////////
//
// Filename: wbscope_tb.cpp
//
// Project: WBScope, a wishbone hosted scope
//
// Purpose: A quick test bench to determine if the wbscope module works.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, 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.
//
// You should have received a copy of the GNU General Public License along
// 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.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#include <stdio.h>
 
#include <verilated.h>
#include <verilated_vcd_c.h>
#include "testb.h"
#include "Vwbscope_tb.h"
 
#define MMUFLAG_RONW 8 // Read only (not writeable)
#define MMUFLAG_ACCS 4 // Accessed
#define MMUFLAG_CCHE 2 // Cachable
#define MMUFLAG_THSP 1 // Page has this context
 
const int BOMBCOUNT = 32,
LGMEMSIZE = 15;
 
class WBSCOPE_TB : public TESTB<Vwbscope_tb> {
bool m_bomb, m_miss, m_err, m_debug;
int m_last_tlb_index;
public:
 
WBSCOPE_TB(void) {
m_debug = true;
m_last_tlb_index = 0;
}
 
void tick(void) {
 
TESTB<Vwbscope_tb>::tick();
 
bool writeout = true;
if ((m_debug)&&(writeout)) {}
}
 
void reset(void) {
m_core->i_rst = 1;
m_core->i_wb_cyc = 0;
m_core->i_wb_stb = 0;
tick();
m_core->i_rst = 0;
}
 
void wb_tick(void) {
m_core->i_wb_cyc = 0;
m_core->i_wb_stb = 0;
tick();
assert(!m_core->o_wb_ack);
}
 
unsigned wb_read(unsigned a) {
unsigned result;
 
printf("WB-READM(%08x)\n", a);
 
m_core->i_wb_cyc = 1;
m_core->i_wb_stb = 1;
m_core->i_wb_we = 0;
m_core->i_wb_addr= (a>>2)&1;
 
// Dont need to check for stalls, since the wbscope never stalls
tick();
 
m_core->i_wb_stb = 0;
 
while(!m_core->o_wb_ack)
tick();
 
result = m_core->o_wb_data;
 
// Release the bus?
m_core->i_wb_cyc = 0;
m_core->i_wb_stb = 0;
 
// Let the bus idle for one cycle
tick();
 
return result;
}
 
void wb_read(unsigned a, int len, unsigned *buf) {
int cnt, rdidx;
 
printf("WB-READM(%08x, %d)\n", a, len);
 
m_core->i_wb_cyc = 1;
m_core->i_wb_stb = 1;
m_core->i_wb_we = 0;
m_core->i_wb_addr = (a>>2)&1;
 
rdidx =0; cnt = 0;
 
do {
tick();
// Normally, we'd increment the address here. For the
// scope, multiple reads only make sense if they are
// from the same address, hence we don't increment the
// address here
// m_core->i_wb_addr += inc;
cnt += 1;
if (m_core->o_wb_ack)
buf[rdidx++] = m_core->o_wb_data;
} while(cnt < len);
 
m_core->i_wb_stb = 0;
 
while(rdidx < len) {
tick();
if (m_core->o_wb_ack)
buf[rdidx++] = m_core->o_wb_data;
}
 
// Release the bus?
m_core->i_wb_cyc = 0;
 
tick();
assert(!m_core->o_wb_ack);
}
 
void wb_write(unsigned a, unsigned v) {
int errcount = 0;
 
printf("WB-WRITEM(%08x) <= %08x\n", a, v);
m_core->i_wb_cyc = 1;
m_core->i_wb_stb = 1;
m_core->i_wb_we = 1;
m_core->i_wb_addr= (a>>2)&1;
m_core->i_wb_data= v;
 
tick();
m_core->i_wb_stb = 0;
 
while(!m_core->o_wb_ack) {
tick();
}
 
tick();
 
// Release the bus?
m_core->i_wb_cyc = 0;
m_core->i_wb_stb = 0;
 
assert(!m_core->o_wb_ack);
}
 
unsigned trigger(void) {
m_core->i_trigger = 1;
wb_tick();
m_core->i_trigger = 0;
printf("TRIGGERED AT %08x\n", m_core->o_data);
return m_core->o_data;
}
 
bool debug(void) const { return m_debug; }
bool debug(bool nxtv) { return m_debug = nxtv; }
};
 
int main(int argc, char **argv) {
Verilated::commandArgs(argc, argv);
WBSCOPE_TB *tb = new WBSCOPE_TB;
unsigned v;
unsigned *buf;
int trigpt;
 
tb->opentrace("wbscope_tb.vcd");
printf("Giving the core 2 cycles to start up\n");
// Before testing, let's give the unit time enough to warm up
tb->reset();
for(int i=0; i<2; i++)
tb->wb_tick();
 
#define WBSCOPE_STATUS 0
#define WBSCOPE_DATA 4
#define WBSCOPE_NORESET 0x80000000
#define WBSCOPE_TRIGGER (WBSCOPE_NO_RESET|0x08000000)
#define WBSCOPE_MANUAL (WBSCOPE_TRIGGER)
#define WBSCOPE_PRIMED 0x10000000
#define WBSCOPE_TRIGGERED 0x20000000
#define WBSCOPE_STOPPED 0x40000000
#define WBSCOPE_DISABLED 0x04000000
#define WBSCOPE_LGLEN(A) ((A>>20)&0x01f)
#define WBSCOPE_LENGTH(A) (1<<(LGLEN(A)))
 
// First test ... read the status register
v = tb->wb_read(WBSCOPE_STATUS);
int ln = WBSCOPE_LGLEN(v);
printf("V = %08x\n", v);
printf("LN = %d, or %d entries\n", ln, (1<<ln));
printf("DLY = %d\n", (v&0xfffff));
if (((1<<ln) < tb->m_tickcount)&&(v&0x10000000)) {
printf("SCOPE is already triggered! ??\n");
goto test_failure;
}
buf = new unsigned[(1<<ln)];
 
for(int i=0; i<(1<<ln); i++)
tb->wb_tick();
 
v = tb->wb_read(WBSCOPE_STATUS);
if ((v&WBSCOPE_PRIMED)==0) {
printf("v = %08x\n", v);
printf("SCOPE hasn\'t primed! ??\n");
goto test_failure;
}
 
tb->trigger();
v = tb->wb_read(WBSCOPE_STATUS);
if ((v&WBSCOPE_TRIGGERED)==0) {
printf("v = %08x\n", v);
printf("SCOPE hasn\'t triggered! ??\n");
goto test_failure;
}
 
while((v & WBSCOPE_STOPPED)==0)
v = tb->wb_read(WBSCOPE_STATUS);
printf("SCOPE has stopped, reading data\n");
 
tb->wb_read(WBSCOPE_DATA, (1<<ln), buf);
for(int i=0; i<(1<<ln); i++) {
printf("%4d: %08x\n", i, buf[i]);
if ((i>0)&&(((buf[i]&0x7fffffff)-(buf[i-1]&0x7fffffff))!=1))
goto test_failure;
}
 
trigpt = (1<<ln)-v&(0x0fffff);
if ((trigpt >= 0)&&(trigpt < (1<<ln))) {
printf("Trigger value = %08x\n", buf[trigpt]);
if (((0x80000000 & buf[trigpt])==0)&&(trigpt>0)) {
printf("Pre-Trigger value = %08x\n", buf[trigpt-1]);
if ((buf[trigpt-1]&0x80000000)==0) {
printf("TRIGGER NOT FOUND\n");
goto test_failure;
}
}
}
 
printf("SUCCESS!!\n");
delete tb;
exit(0);
test_failure:
printf("FAIL-HERE\n");
for(int i=0; i<4; i++)
tb->tick();
printf("TEST FAILED\n");
delete tb;
exit(-1);
}
trunk/bench/cpp Property changes : Added: svn:ignore ## -0,0 +1,2 ## +*_tb +*.vcd Index: trunk/bench/rtl/Makefile =================================================================== --- trunk/bench/rtl/Makefile (nonexistent) +++ trunk/bench/rtl/Makefile (revision 12) @@ -0,0 +1,64 @@ +################################################################################ +# +# Filename: Makefile +# +# Project: WBScope, a wishbone hosted scope +# +# Purpose: This makefile builds a verilator simulation of the rtl +# testbenches necessary to test certain components of the +# wishbone scope using Verilator. It does not make the system within +## Icarus, Vivado or Quartus. +## +## +## Creator: Dan Gisselquist, Ph.D. +## Gisselquist Technology, LLC +## +################################################################################ +## +## Copyright (C) 2015-2017, 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. +## +## You should have received a copy of the GNU General Public License along +## 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 +## for a copy. +## +## License: GPL, v3, as defined and found on www.gnu.org, +## http://www.gnu.org/licenses/gpl.html +## +## +################################################################################ +## +## +.PHONY: all +all: wbscope_tb + +RTLD := ../../rtl +VOBJ := obj_dir + +$(VOBJ)/Vwbscope_tb.cpp: $(RTLD)/wbscope.v wbscope_tb.v + verilator -trace -cc -y $(RTLD) wbscope_tb.v +$(VOBJ)/Vwbscope_tb.h: $(VOBJ)/Vwbscope_tb.cpp + +$(VOBJ)/Vwbscope_tb__ALL.a: $(VOBJ)/Vwbscope_tb.cpp $(VOBJ)/Vwbscope_tb.h + make --no-print-directory --directory=$(VOBJ) -f Vwbscope_tb.mk + +.PHONY: wbscope_tb +wbscope_tb: $(VOBJ)/Vwbscope_tb__ALL.a + +# $(VOBJ)/Vaxiscope_tb.cpp: $(RTLD)/axiscope.v axiscope.v +# verilator -trace -cc -y $(RTLD) wbscope_tb.v +# $(VOBJ)/Vaxiscope_tb.h: $(VOBJ)/Vwbscope_tb.cpp + +.PHONY: clean +clean: + rm -rf $(VOBJ) Index: trunk/bench/rtl/wbscope_tb.v =================================================================== --- trunk/bench/rtl/wbscope_tb.v (nonexistent) +++ trunk/bench/rtl/wbscope_tb.v (revision 12) @@ -0,0 +1,69 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: wbscope_tb.v +// +// Project: WBScope, a wishbone hosted scope +// +// Purpose: +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2015-2017, 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. +// +// You should have received a copy of the GNU General Public License along +// 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 +// for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +module wbscope_tb(i_clk, i_rst, i_trigger, o_data, + i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, + o_wb_ack, o_wb_data, o_interrupt); + input i_clk, i_rst, i_trigger; + output wire [31:0] o_data; + // + input i_wb_cyc, i_wb_stb, i_wb_we; + input i_wb_addr; + input [31:0] i_wb_data; + // + output wire o_wb_ack; + output wire [31:0] o_wb_data; + // + output o_interrupt; + + reg [30:0] counter; + initial counter = 0; + always @(posedge i_clk) + counter <= counter + 1'b1; + + assign o_data = { i_trigger, counter }; + + wire wb_stall_ignored; + + wbscope #(5'd6, 32, 1) + scope(i_clk, 1'b1, i_trigger, o_data, + i_clk, i_wb_cyc, i_wb_stb, i_wb_we, + i_wb_addr, i_wb_data, + o_wb_ack, wb_stall_ignored, o_wb_data, + o_interrupt); + +endmodule Index: trunk/bench/rtl =================================================================== --- trunk/bench/rtl (nonexistent) +++ trunk/bench/rtl (revision 12)
trunk/bench/rtl Property changes : Added: svn:ignore ## -0,0 +1 ## +obj_dir Index: trunk/doc/Makefile =================================================================== --- trunk/doc/Makefile (revision 11) +++ trunk/doc/Makefile (revision 12) @@ -15,6 +15,6 @@ ps2pdf -dAutoRotatePages=/All spec.ps spec.pdf rm $(DSRC)/spec.dvi $(DSRC)/spec.log rm $(DSRC)/spec.aux $(DSRC)/spec.toc - rm $(DSRC)/spec.lot # $(DSRC)/spec.lof + rm $(DSRC)/spec.lot $(DSRC)/spec.out rm spec.ps
/trunk/doc/gpl-3.0.pdf Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
/trunk/doc/spec.pdf Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream
/trunk/doc/src/gqtekspec.cls
29,6 → 29,8
\usepackage[dvips]{pstricks}
\usepackage{hhline}
\usepackage{colortbl}
\definecolor{webgreen}{rgb}{0,0.5,0}
\usepackage[dvips,colorlinks=true,linkcolor=webgreen]{hyperref}
\newdateformat{headerdate}{\THEYEAR/\twodigit{\THEMONTH}/\twodigit{\THEDAY}}
\setlength{\hoffset}{0.25in}
\setlength{\voffset}{-0.5in}
/trunk/doc/src/spec.tex
45,7 → 45,7
\project{Wishbone Scope}
\title{Specification}
\author{Dan Gisselquist, Ph.D.}
\email{dgisselq (at) opencores.org}
\email{dgisselq (at) ieee.org}
\revision{Rev.~0.1}
\begin{document}
\pagestyle{gqtekspecplain}
64,7 → 64,7
for more details.
 
You should have received a copy of the GNU General Public License along
with this program. If not, see \hbox{<http://www.gnu.org/licenses/>} for a
with this program. If not, see \texttt{http://www.gnu.org/licenses/} for a
copy.
\end{license}
\begin{revisionhistory}
341,7 → 341,7
clock. That is, if the {\tt i\_wb\_stb} line is high on one clock, the
{\tt i\_wb\_ack} line will be high the next. Further, the {\tt o\_wb\_stall}
line is tied to zero.
\chapter{IO Ports}
\chapter{I/O Ports}\label{ch:ioports}
 
The ports are listed in Table.~\ref{tbl:ioports}.
\begin{table}[htbp]
/trunk/rtl/axi4lscope.v
0,0 → 1,562
`timescale 1 ns / 1 ps
////////////////////////////////////////////////////////////////////////////////
//
// Filename: axi4lscope.v
//
// Project: FPGA Library of Routines
//
// Purpose: This is a generic/library routine for providing a bus accessed
// 'scope' or (perhaps more appropriately) a bus accessed logic analyzer.
// The general operation is such that this 'scope' can record and report
// on any 32 bit value transiting through the FPGA. Once started and
// reset, the scope records a copy of the input data every time the clock
// ticks with the circuit enabled. That is, it records these values up
// until the trigger. Once the trigger goes high, the scope will record
// for bw_holdoff more counts before stopping. Values may then be read
// from the buffer, oldest to most recent. After reading, the scope may
// then be reset for another run.
//
// In general, therefore, operation happens in this fashion:
// 1. A reset is issued.
// 2. Recording starts, in a circular buffer, and continues until
// 3. The trigger line is asserted.
// The scope registers the asserted trigger by setting
// the 'o_triggered' output flag.
// 4. A counter then ticks until the last value is written
// The scope registers that it has stopped recording by
// setting the 'o_stopped' output flag.
// 5. The scope recording is then paused until the next reset.
// 6. While stopped, the CPU can read the data from the scope
// 7. -- oldest to most recent
// 8. -- one value per i_rd&i_clk
// 9. Writes to the data register reset the address to the
// beginning of the buffer
//
// Although the data width DW is parameterized, it is not very changable,
// since the width is tied to the width of the data bus, as is the
// control word. Therefore changing the data width would require changing
// the interface. It's doable, but it would be a change to the interface.
//
// The SYNCHRONOUS parameter turns on and off meta-stability
// synchronization. Ideally a wishbone scope able to handle one or two
// clocks would have a changing number of ports as this SYNCHRONOUS
// parameter changed. Other than running another script to modify
// this, I don't know how to do that so ... we'll just leave it running
// off of two clocks or not.
//
//
// Internal to this routine, registers and wires are named with one of the
// following prefixes:
//
// i_ An input port to the routine
// o_ An output port of the routine
// br_ A register, controlled by the bus clock
// dr_ A register, controlled by the data clock
// bw_ A wire/net, controlled by the bus clock
// dw_ A wire/net, controlled by the data clock
//
// And, of course, since AXI wants to be particular about their port
// naming conventions, anything beginning with
//
// S_AXI_
//
// is a signal associated with this function as an AXI slave.
//
//
// 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.
//
// You should have received a copy of the GNU General Public License along
// 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.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
module axi4lscope
#(
// Users to add parameters here
parameter LGMEM = 5'd10,
parameter BUSW = 32,
parameter SYNCHRONOUS=1,
parameter DEFAULT_HOLDOFF = ((1<<(LGMEM-1))-4),
// User parameters ends
// DO NOT EDIT BELOW THIS LINE ---------------------
// Do not modify the parameters beyond this line
// Width of S_AXI data bus
parameter integer C_S_AXI_DATA_WIDTH = 32,
// Width of S_AXI address bus
parameter integer C_S_AXI_ADDR_WIDTH = 4
)
(
// Users to add ports here
input wire i_clk, // The data clock, can be set to ACLK
input wire i_ce, // = '1' when recordable data is present
input wire i_trigger,// = '1' when interesting event hapns
input wire [31:0] i_data,
output wire o_interrupt, // ='1' when scope has stopped
// User ports ends
// DO NOT EDIT BELOW THIS LINE ---------------------
// Do not modify the ports beyond this line
// Global Clock Signal
input wire S_AXI_ACLK,
// Global Reset Signal. This Signal is Active LOW
input wire S_AXI_ARESETN,
// Write address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_AWADDR,
// Write channel Protection type. This signal indicates the
// privilege and security level of the transaction, and whether
// the transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_AWPROT,
// Write address valid. This signal indicates that the master
// signaling valid write address and control information.
input wire S_AXI_AWVALID,
// Write address ready. This signal indicates that the slave
// is ready to accept an address and associated control signals.
output wire S_AXI_AWREADY,
// Write data (issued by master, acceped by Slave)
input wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_WDATA,
// Write strobes. This signal indicates which byte lanes hold
// valid data. There is one write strobe bit for each eight
// bits of the write data bus.
input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0] S_AXI_WSTRB,
// Write valid. This signal indicates that valid write
// data and strobes are available.
input wire S_AXI_WVALID,
// Write ready. This signal indicates that the slave
// can accept the write data.
output wire S_AXI_WREADY,
// Write response. This signal indicates the status
// of the write transaction.
output wire [1 : 0] S_AXI_BRESP,
// Write response valid. This signal indicates that the channel
// is signaling a valid write response.
output wire S_AXI_BVALID,
// Response ready. This signal indicates that the master
// can accept a write response.
input wire S_AXI_BREADY,
// Read address (issued by master, acceped by Slave)
input wire [C_S_AXI_ADDR_WIDTH-1 : 0] S_AXI_ARADDR,
// Protection type. This signal indicates the privilege
// and security level of the transaction, and whether the
// transaction is a data access or an instruction access.
input wire [2 : 0] S_AXI_ARPROT,
// Read address valid. This signal indicates that the channel
// is signaling valid read address and control information.
input wire S_AXI_ARVALID,
// Read address ready. This signal indicates that the slave is
// ready to accept an address and associated control signals.
output wire S_AXI_ARREADY,
// Read data (issued by slave)
output wire [C_S_AXI_DATA_WIDTH-1 : 0] S_AXI_RDATA,
// Read response. This signal indicates the status of the
// read transfer.
output wire [1 : 0] S_AXI_RRESP,
// Read valid. This signal indicates that the channel is
// signaling the required read data.
output wire S_AXI_RVALID,
// Read ready. This signal indicates that the master can
// accept the read data and response information.
input wire S_AXI_RREADY
// DO NOT EDIT ABOVE THIS LINE ---------------------
);
 
// AXI4LITE signals
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_awaddr;
reg axi_awready;
reg axi_wready;
// reg [1 : 0] axi_bresp;
reg axi_bvalid;
reg [C_S_AXI_ADDR_WIDTH-1 : 0] axi_araddr;
reg axi_arready;
reg [C_S_AXI_DATA_WIDTH-1 : 0] axi_rdata;
// reg [1 : 0] axi_rresp;
reg axi_rvalid;
 
 
///////////////////////////////////////////////////
//
// Decode and handle the AXI/Bus signaling
//
///////////////////////////////////////////////////
//
// Sadly, the AXI bus is *way* more complicated to
// deal with than it needs to be. Still, we offer
// the following as a simple means of dealing with
// it. The majority of the code in this section
// comes directly from a Xilinx/Vivado generated
// file.
//
// Gisselquist Technology, LLC, claims no copyright
// or ownership of this section of the code.
//
wire i_reset;
assign i_reset = !S_AXI_ARESETN;
 
always @(posedge S_AXI_ACLK)
if (i_reset)
axi_awready <= 1'b0;
else if ((!axi_awready)&&(S_AXI_AWVALID)&&(S_AXI_WVALID))
axi_awready <= 1'b1;
else
axi_awready <= 1'b0;
assign S_AXI_AWREADY = axi_awready;
 
always @(posedge S_AXI_ACLK)
if ((!axi_awready)&&(S_AXI_AWVALID)&&(S_AXI_WVALID))
axi_awaddr <= S_AXI_AWADDR;
 
always @(posedge S_AXI_ACLK)
if (i_reset)
axi_wready <= 1'b0;
else if ((!axi_wready)&&(S_AXI_WVALID)&&(S_AXI_AWVALID))
axi_wready <= 1'b1;
else
axi_wready <= 1'b0;
assign S_AXI_WREADY = axi_wready;
 
wire write_stb;
 
always @(posedge S_AXI_ACLK)
if (i_reset)
begin
axi_bvalid <= 0;
// axi_bresp <= 2'b00;
end else if ((~axi_bvalid)&&(write_stb))
begin
axi_bvalid <= 1'b1;
// axi_bresp <= 2'b00; // 'Okay' response
end else if ((S_AXI_BREADY)&&(axi_bvalid))
axi_bvalid <= 1'b0;
assign S_AXI_BRESP = 2'b00; // An 'OKAY' response
assign S_AXI_BVALID= axi_bvalid;
 
 
 
always @(posedge S_AXI_ACLK)
if (i_reset)
begin
axi_arready <= 1'b0;
axi_araddr <= 0;
end else if ((!axi_arready)&&(S_AXI_ARVALID))
begin
axi_arready <= 1'b1;
axi_araddr <= S_AXI_ARADDR;
end else
axi_arready <= 1'b0;
assign S_AXI_ARREADY = axi_arready;
 
always @(posedge S_AXI_ACLK)
if (i_reset)
begin
axi_rvalid <= 0;
// axi_rresp <= 0;
end else if ((axi_arready)&&(S_AXI_ARVALID)&&(!axi_rvalid))
begin
axi_rvalid <= 1'b0;
// axi_rresp <= 2'b00;
end else if ((axi_rvalid)&&(S_AXI_RREADY))
axi_rvalid <= 1'b0;
assign S_AXI_RVALID = axi_rvalid;
assign S_AXI_RRESP = 2'b00;
 
 
 
 
///////////////////////////////////////////////////
//
// Final simplification of the AXI code
//
///////////////////////////////////////////////////
//
// Now that we've provided all of the bus signaling
// above, can we make any sense of it?
//
// The following wires are here to provide some
// simplification of the complex bus protocol. In
// particular, are we reading or writing during this
// clock? The two *should* be mutually exclusive
// (i.e., you *shouldn't* be able to both read and
// write on the same clock) ... but Xilinx's default
// implementation does nothing to ensure that this
// would be the case.
//
// From here on down, Gisselquist Technology, LLC,
// claims a copyright on the code.
//
wire read_from_data;
assign read_from_data = (S_AXI_ARVALID)&&(S_AXI_ARREADY)
&&(axi_araddr[0]);
 
assign write_stb = ((axi_awready)&&(S_AXI_AWVALID)
&&(axi_wready)&&(S_AXI_WVALID));
wire write_to_control;
assign write_to_control = (write_stb)&&(!axi_awaddr[0]);
 
 
wire [31:0] i_wb_data;
assign i_wb_data = S_AXI_WDATA;
 
 
///////////////////////////////////////////////////
//
// The actual SCOPE
//
///////////////////////////////////////////////////
//
// Now that we've finished reading/writing from the
// bus, ... or at least acknowledging reads and
// writes from and to the bus--even if they haven't
// happened yet, now we implement our actual scope.
// This includes implementing the actual reads/writes
// from/to the bus.
//
// From here on down, is the heart of the scope itself.
//
reg [(LGMEM-1):0] raddr;
reg [(BUSW-1):0] mem[0:((1<<LGMEM)-1)];
 
// Our status/config register
wire bw_reset_request, bw_manual_trigger,
bw_disable_trigger, bw_reset_complete;
reg [22:0] br_config;
wire [19:0] bw_holdoff;
initial br_config = DEFAULT_HOLDOFF;
always @(posedge S_AXI_ACLK)
if (write_to_control)
begin
br_config <= { i_wb_data[31],
(i_wb_data[27]),
i_wb_data[26],
i_wb_data[19:0] };
end else if (bw_reset_complete)
br_config[22] <= 1'b1;
assign bw_reset_request = (~br_config[22]);
assign bw_manual_trigger = (br_config[21]);
assign bw_disable_trigger = (br_config[20]);
assign bw_holdoff = br_config[19:0];
 
wire dw_reset, dw_manual_trigger, dw_disable_trigger;
generate
if (SYNCHRONOUS > 0)
begin
assign dw_reset = bw_reset_request;
assign dw_manual_trigger = bw_manual_trigger;
assign dw_disable_trigger = bw_disable_trigger;
assign bw_reset_complete = bw_reset_request;
end else begin
reg r_reset_complete;
(* ASYNC_REG = "TRUE" *) reg [2:0] q_iflags;
reg [2:0] r_iflags;
 
// Resets are synchronous to the bus clock, not the data clock
// so do a clock transfer here
initial q_iflags = 3'b000;
initial r_reset_complete = 1'b0;
always @(posedge i_clk)
begin
q_iflags <= { bw_reset_request, bw_manual_trigger, bw_disable_trigger };
r_iflags <= q_iflags;
r_reset_complete <= (dw_reset);
end
 
assign dw_reset = r_iflags[2];
assign dw_manual_trigger = r_iflags[1];
assign dw_disable_trigger = r_iflags[0];
 
(* ASYNC_REG = "TRUE" *) reg q_reset_complete;
reg qq_reset_complete;
// Pass an acknowledgement back from the data clock to the bus
// clock that the reset has been accomplished
initial q_reset_complete = 1'b0;
initial qq_reset_complete = 1'b0;
always @(posedge S_AXI_ACLK)
begin
q_reset_complete <= r_reset_complete;
qq_reset_complete <= q_reset_complete;
end
 
assign bw_reset_complete = qq_reset_complete;
end endgenerate
 
//
// Set up the trigger
//
//
// Write with the i-clk, or input clock. All outputs read with the
// WISHBONE-clk, or S_AXI_ACLK clock.
reg dr_triggered, dr_primed;
wire dw_trigger;
assign dw_trigger = (dr_primed)&&(
((i_trigger)&&(~dw_disable_trigger))
||(dr_triggered)
||(dw_manual_trigger));
initial dr_triggered = 1'b0;
always @(posedge i_clk)
if (dw_reset)
dr_triggered <= 1'b0;
else if ((i_ce)&&(dw_trigger))
dr_triggered <= 1'b1;
 
//
// Determine when memory is full and capture is complete
//
// Writes take place on the data clock
reg dr_stopped;
reg [19:0] counter; // This is unsigned
initial dr_stopped = 1'b0;
initial counter = 20'h0000;
always @(posedge i_clk)
if (dw_reset)
begin
counter <= 0;
dr_stopped <= 1'b0;
end else if ((i_ce)&&(dr_triggered))
begin // MUST BE a < and not <=, so that we can keep this w/in
// 20 bits. Else we'd need to add a bit to comparison
// here.
if (counter < bw_holdoff)
counter <= counter + 20'h01;
else
dr_stopped <= 1'b1;
end
 
//
// Actually do our writes to memory. Record, via 'primed' when
// the memory is full.
//
// The 'waddr' address that we are using really crosses two clock
// domains. While writing and changing, it's in the data clock
// domain. Once stopped, it becomes part of the bus clock domain.
// The clock transfer on the stopped line handles the clock
// transfer for these signals.
//
reg [(LGMEM-1):0] waddr;
initial waddr = {(LGMEM){1'b0}};
initial dr_primed = 1'b0;
always @(posedge i_clk)
if (dw_reset) // For simulation purposes, supply a valid value
begin
waddr <= 0; // upon reset.
dr_primed <= 1'b0;
end else if ((i_ce)&&((~dr_triggered)||(counter < bw_holdoff)))
begin
// mem[waddr] <= i_data;
waddr <= waddr + {{(LGMEM-1){1'b0}},1'b1};
dr_primed <= (dr_primed)||(&waddr);
end
always @(posedge i_clk)
if ((i_ce)&&((~dr_triggered)||(counter < bw_holdoff)))
mem[waddr] <= i_data;
 
//
// Clock transfer of the status signals
//
wire bw_stopped, bw_triggered, bw_primed;
generate
if (SYNCHRONOUS > 0)
begin
assign bw_stopped = dr_stopped;
assign bw_triggered = dr_triggered;
assign bw_primed = dr_primed;
end else begin
// These aren't a problem, since none of these are strobe
// signals. They goes from low to high, and then stays high
// for many clocks. Swapping is thus easy--two flip flops to
// protect against meta-stability and we're done.
//
(* ASYNC_REG = "TRUE" *) reg [2:0] q_oflags;
reg [2:0] r_oflags;
initial q_oflags = 3'h0;
initial r_oflags = 3'h0;
always @(posedge S_AXI_ACLK)
if (bw_reset_request)
begin
q_oflags <= 3'h0;
r_oflags <= 3'h0;
end else begin
q_oflags <= { dr_stopped, dr_triggered, dr_primed };
r_oflags <= q_oflags;
end
 
assign bw_stopped = r_oflags[2];
assign bw_triggered = r_oflags[1];
assign bw_primed = r_oflags[0];
end endgenerate
 
// Reads use the bus clock
reg br_wb_ack;
initial br_wb_ack = 1'b0;
always @(posedge S_AXI_ACLK)
begin
if ((bw_reset_request)||((write_stb)&&(axi_awaddr[0])))
raddr <= 0;
else if ((read_from_data)&&(bw_stopped))
// Data read ... only takes place when stopped
raddr <= raddr + {{(LGMEM-1){1'b0}},1'b1};
end
 
 
reg [31:0] nxt_mem;
always @(posedge S_AXI_ACLK)
nxt_mem <= mem[raddr+waddr+ ((read_from_data) ?
{{(LGMEM-1){1'b0}},1'b1} : { (LGMEM){1'b0}} )];
 
 
 
 
 
 
 
wire [4:0] bw_lgmem;
assign bw_lgmem = LGMEM;
always @(posedge S_AXI_ACLK)
if (~axi_araddr[0]) // Control register read
axi_rdata <= { bw_reset_request,
bw_stopped,
bw_triggered,
bw_primed,
bw_manual_trigger,
bw_disable_trigger,
(raddr == {(LGMEM){1'b0}}),
bw_lgmem,
bw_holdoff };
else if (~bw_stopped) // read, prior to stopping
axi_rdata <= i_data;
else // if (i_wb_addr) // Read from FIFO memory
axi_rdata <= nxt_mem; // mem[raddr+waddr];
assign S_AXI_RDATA = axi_rdata;
 
 
reg br_level_interrupt;
initial br_level_interrupt = 1'b0;
assign o_interrupt = (bw_stopped)&&(~bw_disable_trigger)
&&(~br_level_interrupt);
always @(posedge S_AXI_ACLK)
if ((bw_reset_complete)||(bw_reset_request))
br_level_interrupt<= 1'b0;
else
br_level_interrupt<= (bw_stopped)&&(~bw_disable_trigger);
 
endmodule
 
 
/trunk/rtl/wbscopc.v
1,8 → 1,8
///////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Filename: wbscopc.v
//
// Project: FPGA Library of Routines
// Project: WBScope, a wishbone hosted scope
//
// Purpose: This scope is identical in function to the wishbone scope
// found in wbscope, save that the output is compressed and that (as a
61,9 → 61,9
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
///////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015, Gisselquist Technology, LLC
// Copyright (C) 2015,2017, 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
76,7 → 76,7
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory, run make with no
// 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.
//
84,7 → 84,7
// http://www.gnu.org/licenses/gpl.html
//
//
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//
module wbscopc(i_clk, i_ce, i_trigger, i_data,
91,7 → 91,7
i_wb_clk, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data,
o_wb_ack, o_wb_stall, o_wb_data,
o_interrupt);
parameter LGMEM = 5'd10, NELM=32, BUSW = 32, SYNCHRONOUS=1;
parameter LGMEM = 5'd10, NELM=31, BUSW = 32, SYNCHRONOUS=1;
// The input signals that we wish to record
input i_clk, i_ce, i_trigger;
input [(NELM-1):0] i_data;
113,7 → 113,7
// When is the full scope reset? Capture that reset bit from any
// write.
wire lcl_reset;
assign lcl_reset = (i_wb_cyc)&&(i_wb_stb)&&(~i_wb_addr)&&(i_wb_we)
assign lcl_reset = (i_wb_stb)&&(~i_wb_addr)&&(i_wb_we)
&&(~i_wb_data[31]);
 
// A big part of this scope is the 'address' of any particular
138,6 → 138,14
else
ck_addr <= ck_addr + 1;
 
wire [(BUSW-2):0] w_data;
generate
if (NELM == BUSW-1)
assign w_data = i_data;
else
assign w_data = { {(BUSW-NELM-1){1'b0}}, i_data };
endgenerate
//
// To do our compression, we keep track of two registers: the most
// recent data to the device (imm_ prefix) and the data from one
159,7 → 167,7
lst_dat <= 0;
end else if ((i_ce)&&(i_data != lst_dat))
begin
imm_val <= { {(BUSW-1-NELM){1'b0}}, i_data };
imm_val <= w_data;
imm_adr <= 1'b0;
lst_val <= imm_val;
lst_adr <= imm_adr;
/trunk/rtl/wbscope.v
1,22 → 1,21
///////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Filename: wbscope.v
//
// Project: FPGA Library of Routines
// Project: WBScope, a wishbone hosted scope
//
// Purpose: This is a generic/library routine for providing a bus accessed
// 'scope' or (perhaps more appropriately) a bus accessed logic
// analyzer. The general operation is such that this 'scope' can
// record and report on any 32 bit value transiting through the
// FPGA. Once started and reset, the scope records a copy of the
// input data every time the clock ticks with the circuit enabled.
// That is, it records these values up until the trigger. Once
// the trigger goes high, the scope will record for bw_holdoff
// more counts before stopping. Values may then be read from the
// buffer, oldest to most recent. After reading, the scope may
// then be reset for another run.
// 'scope' or (perhaps more appropriately) a bus accessed logic analyzer.
// The general operation is such that this 'scope' can record and report
// on any 32 bit value transiting through the FPGA. Once started and
// reset, the scope records a copy of the input data every time the clock
// ticks with the circuit enabled. That is, it records these values up
// until the trigger. Once the trigger goes high, the scope will record
// for br_holdoff more counts before stopping. Values may then be read
// from the buffer, oldest to most recent. After reading, the scope may
// then be reset for another run.
//
// In general, therefore, operation happens in this fashion:
// In general, therefore, operation happens in this fashion:
// 1. A reset is issued.
// 2. Recording starts, in a circular buffer, and continues until
// 3. The trigger line is asserted.
58,9 → 57,9
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
///////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015, Gisselquist Technology, LLC
// Copyright (C) 2015-2017, 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
73,7 → 72,7
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. (It's in the $(ROOT)/doc directory, run make with no
// 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.
//
81,12 → 80,18
// http://www.gnu.org/licenses/gpl.html
//
//
/////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
//
module wbscope(i_clk, i_ce, i_trigger, i_data,
i_wb_clk, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data,
o_wb_ack, o_wb_stall, o_wb_data,
o_interrupt);
parameter LGMEM = 5'd10, BUSW = 32, SYNCHRONOUS=1;
parameter [4:0] LGMEM = 5'd10;
parameter BUSW = 32;
parameter [0:0] SYNCHRONOUS=1;
parameter HOLDOFFBITS = 20;
parameter [(HOLDOFFBITS-1):0] DEFAULT_HOLDOFF = ((1<<(LGMEM-1))-4);
// The input signals that we wish to record
input i_clk, i_ce, i_trigger;
input [(BUSW-1):0] i_data;
107,23 → 112,25
// Our status/config register
wire bw_reset_request, bw_manual_trigger,
bw_disable_trigger, bw_reset_complete;
reg [22:0] br_config;
wire [19:0] bw_holdoff;
initial br_config = ((1<<(LGMEM-1))-4);
reg [2:0] br_config;
reg [(HOLDOFFBITS-1):0] br_holdoff;
initial br_config = 3'b0;
initial br_holdoff = DEFAULT_HOLDOFF;
always @(posedge i_wb_clk)
if ((i_wb_cyc)&&(i_wb_stb)&&(~i_wb_addr))
if ((i_wb_stb)&&(!i_wb_addr))
begin
if (i_wb_we)
begin
br_config <= { i_wb_data[31],
(i_wb_data[27]),
i_wb_data[26],
i_wb_data[19:0] };
i_wb_data[27],
i_wb_data[26] };
br_holdoff = i_wb_data[(HOLDOFFBITS-1):0];
end
end else if (bw_reset_complete)
br_config[22] <= 1'b1;
assign bw_reset_request = (~br_config[22]);
assign bw_manual_trigger = (br_config[21]);
assign bw_disable_trigger = (br_config[20]);
assign bw_holdoff = br_config[19:0];
br_config[2] <= 1'b1;
assign bw_reset_request = (!br_config[2]);
assign bw_manual_trigger = (br_config[1]);
assign bw_disable_trigger = (br_config[0]);
 
wire dw_reset, dw_manual_trigger, dw_disable_trigger;
generate
135,7 → 142,8
assign bw_reset_complete = bw_reset_request;
end else begin
reg r_reset_complete;
reg [2:0] r_iflags, q_iflags;
(* ASYNC_REG = "TRUE" *) reg [2:0] q_iflags;
reg [2:0] r_iflags;
 
// Resets are synchronous to the bus clock, not the data clock
// so do a clock transfer here
152,7 → 160,8
assign dw_manual_trigger = r_iflags[1];
assign dw_disable_trigger = r_iflags[0];
 
reg q_reset_complete, qq_reset_complete;
(* ASYNC_REG = "TRUE" *) reg q_reset_complete;
reg qq_reset_complete;
// Pass an acknowledgement back from the data clock to the bus
// clock that the reset has been accomplished
initial q_reset_complete = 1'b0;
175,8 → 184,7
reg dr_triggered, dr_primed;
wire dw_trigger;
assign dw_trigger = (dr_primed)&&(
((i_trigger)&&(~dw_disable_trigger))
||(dr_triggered)
((i_trigger)&&(!dw_disable_trigger))
||(dw_manual_trigger));
initial dr_triggered = 1'b0;
always @(posedge i_clk)
189,24 → 197,26
// Determine when memory is full and capture is complete
//
// Writes take place on the data clock
// The counter is unsigned
(* ASYNC_REG="TRUE" *) reg [(HOLDOFFBITS-1):0] counter;
 
reg dr_stopped;
reg [19:0] counter; // This is unsigned
initial dr_stopped = 1'b0;
initial counter = 20'h0000;
initial counter = 0;
always @(posedge i_clk)
if (dw_reset)
begin
counter <= 0;
dr_stopped <= 1'b0;
end else if ((i_ce)&&(dr_triggered))
else if ((i_ce)&&(dr_triggered)&&(!dr_stopped))
begin // MUST BE a < and not <=, so that we can keep this w/in
// 20 bits. Else we'd need to add a bit to comparison
// here.
if (counter < bw_holdoff)
counter <= counter + 20'h01;
else
dr_stopped <= 1'b1;
counter <= counter + 1'b1;
end
always @(posedge i_clk)
if ((!dr_triggered)||(dw_reset))
dr_stopped <= 1'b0;
else
dr_stopped <= (counter >= br_holdoff);
 
//
// Actually do our writes to memory. Record, via 'primed' when
226,7 → 236,7
begin
waddr <= 0; // upon reset.
dr_primed <= 1'b0;
end else if ((i_ce)&&((~dr_triggered)||(counter < bw_holdoff)))
end else if ((i_ce)&&(!dr_stopped))
begin
// mem[waddr] <= i_data;
waddr <= waddr + {{(LGMEM-1){1'b0}},1'b1};
233,7 → 243,7
dr_primed <= (dr_primed)||(&waddr);
end
always @(posedge i_clk)
if ((i_ce)&&((~dr_triggered)||(counter < bw_holdoff)))
if ((i_ce)&&(!dr_stopped))
mem[waddr] <= i_data;
 
//
252,7 → 262,8
// for many clocks. Swapping is thus easy--two flip flops to
// protect against meta-stability and we're done.
//
reg [2:0] q_oflags, r_oflags;
(* ASYNC_REG = "TRUE" *) reg [2:0] q_oflags;
reg [2:0] r_oflags;
initial q_oflags = 3'h0;
initial r_oflags = 3'h0;
always @(posedge i_wb_clk)
274,16 → 285,16
reg br_wb_ack;
initial br_wb_ack = 1'b0;
wire bw_cyc_stb;
assign bw_cyc_stb = ((i_wb_cyc)&&(i_wb_stb));
assign bw_cyc_stb = (i_wb_stb);
always @(posedge i_wb_clk)
begin
if ((bw_reset_request)
||((bw_cyc_stb)&&(i_wb_addr)&&(i_wb_we)))
raddr <= 0;
else if ((bw_cyc_stb)&&(i_wb_addr)&&(~i_wb_we)&&(bw_stopped))
raddr <= raddr + {{(LGMEM-1){1'b0}},1'b1}; // Data read, when stopped
else if ((bw_cyc_stb)&&(i_wb_addr)&&(!i_wb_we)&&(bw_stopped))
raddr <= raddr + 1'b1; // Data read, when stopped
 
if ((bw_cyc_stb)&&(~i_wb_we))
if ((bw_cyc_stb)&&(!i_wb_we))
begin // Read from the bus
br_wb_ack <= 1'b1;
end else if ((bw_cyc_stb)&&(i_wb_we))
296,13 → 307,19
reg [31:0] nxt_mem;
always @(posedge i_wb_clk)
nxt_mem <= mem[raddr+waddr+
(((bw_cyc_stb)&&(i_wb_addr)&&(~i_wb_we)) ?
(((bw_cyc_stb)&&(i_wb_addr)&&(!i_wb_we)) ?
{{(LGMEM-1){1'b0}},1'b1} : { (LGMEM){1'b0}} )];
 
wire [19:0] full_holdoff;
assign full_holdoff[(HOLDOFFBITS-1):0] = br_holdoff;
generate if (HOLDOFFBITS < 20)
assign full_holdoff[19:(HOLDOFFBITS)] = 0;
endgenerate
 
wire [4:0] bw_lgmem;
assign bw_lgmem = LGMEM;
always @(posedge i_wb_clk)
if (~i_wb_addr) // Control register read
if (!i_wb_addr) // Control register read
o_wb_data <= { bw_reset_request,
bw_stopped,
bw_triggered,
311,8 → 328,8
bw_disable_trigger,
(raddr == {(LGMEM){1'b0}}),
bw_lgmem,
bw_holdoff };
else if (~bw_stopped) // read, prior to stopping
full_holdoff };
else if (!bw_stopped) // read, prior to stopping
o_wb_data <= i_data;
else // if (i_wb_addr) // Read from FIFO memory
o_wb_data <= nxt_mem; // mem[raddr+waddr];
322,12 → 339,12
 
reg br_level_interrupt;
initial br_level_interrupt = 1'b0;
assign o_interrupt = (bw_stopped)&&(~bw_disable_trigger)
&&(~br_level_interrupt);
assign o_interrupt = (bw_stopped)&&(!bw_disable_trigger)
&&(!br_level_interrupt);
always @(posedge i_wb_clk)
if ((bw_reset_complete)||(bw_reset_request))
br_level_interrupt<= 1'b0;
else
br_level_interrupt<= (bw_stopped)&&(~bw_disable_trigger);
br_level_interrupt<= (bw_stopped)&&(!bw_disable_trigger);
 
endmodule
trunk/rtl Property changes : Added: svn:ignore ## -0,0 +1 ## +obj_dir Index: trunk/sw/cfgscope.cpp =================================================================== --- trunk/sw/cfgscope.cpp (revision 11) +++ trunk/sw/cfgscope.cpp (revision 12) @@ -1,31 +1,30 @@ -/////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // // Filename: cfgscope.cpp // -// Project: FPGA library development (Basys-3 development board) +// Project: WBScope, a wishbone hosted scope // // Purpose: To read out, and decompose, the results of the wishbone scope // as applied to the ICAPE2 interaction. // -// This is provided together with the wbscope project as an -// example of what might be done with the wishbone scope. -// The intermediate details, though, between this and the -// wishbone scope are not part of the wishbone scope project. +// This is provided together with the wbscope project as an example of +// what might be done with the wishbone scope. The intermediate details, +// though, between this and the wishbone scope are not part of the +// wishbone scope project. // -// Using this particular scope made it a *lot* easier to get the -// ICAPE2 interface up and running, since I was able to see what -// was going right (or wrong) with the interface as I was -// developing it. Sure, it would've been better to get it to work -// under a simulator instead of with the scope, but not being -// certain of how the interface was supposed to work made building -// a simulator difficult. +// Using this particular scope made it a *lot* easier to get the ICAPE2 +// interface up and running, since I was able to see what was going right +// (or wrong) with the interface as I was developing it. Sure, it +// would've been better to get it to work under a simulator instead of +// with the scope, but not being certain of how the interface was +// supposed to work made building a simulator difficult. // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // -/////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2015, Gisselquist Technology, LLC +// Copyright (C) 2015-2017, 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 @@ -38,7 +37,7 @@ // for more details. // // You should have received a copy of the GNU General Public License along -// with this program. (It's in the $(ROOT)/doc directory, run make with no +// 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 // for a copy. // @@ -46,36 +45,76 @@ // http://www.gnu.org/licenses/gpl.html // // -/////////////////////////////////////////////////////////////////////////// -#include -#include -#include -#include -#include -#include -#include -#include +//////////////////////////////////////////////////////////////////////////////// +// +// +#include "devbus.h" +#include "scopecls.h" -#include "port.h" -#include "llcomms.h" // This defines how we talk to the device over wishbone -#include "regdefs.h" +// +// CFGSCOPE +// +// When you wish to build your own scope, you'll need to build a version of this +// class to do so. This class has two particular functions to it: one +// (optional) one to define the traces used incase we wish to split these apart +// for output to a VCD file. The other function is for use with debug-by-printf +// approaches. As a result, it provides for a more flexible (textual) output. +// +class CFGSCOPE : public SCOPE { -// Here are the two registers needed for accessing our scope: A control register -// and a data register. -#define WBSCOPE R_CFGSCOPE -#define WBSCOPEDATA R_CFGSCOPED + virtual void define_traces(void) { + // Heres the interface for VCD files: We need to tell the VCD + // writer the names of all of our traces, how many bits each + // trace uses, and where the location of the value exists within + // the 32-bit trace word. + register_trace("cs_n", 1, 31); + register_trace("we_n", 1, 30); + register_trace("code", 6, 24); + register_trace("value", 24, 0); + } -// -// The DEVBUS structure encapsulates wishbone accesses, so that this code can -// access the wishbone bus on the FPGA. -DEVBUS *m_fpga; -void closeup(int v) { - m_fpga->kill(); - exit(0); -} + // + // decode + // + // Decode the value to the standard-output stream. How you decode this + // value is up to you. Prior to the value being printed, a prefix + // identifying the clock number (as counted by the scope, with the + // internal clock enable on), and the raw value will be printed out. + // Further, after doing whatever printing you wish to do here, a newline + // will be printed before going to the next value. + // + virtual void decode(DEVBUS::BUSW v) const { + // Now, let's decompose our 32-bit wires into something ... + // meaningful and dump it to stdout. This section will change + // from project to project, scope to scope, depending on what + // wires are placed into the scope. + printf("%s %s ", (v&0x80000000)?" ":"CS", + (v&0x40000000)?"RD":"WR"); + unsigned cw = (v>>24)&0x03f; + switch(cw) { + case 0x20: printf("DUMMY"); break; + case 0x10: printf("NOOP "); break; + case 0x08: printf("SYNC "); break; + case 0x04: printf("CMD "); break; + case 0x02: printf("IPROG"); break; + case 0x01: printf("DSYNC"); break; + default: printf("OTHER"); break; + } + printf(" -> %02x", v & 0x0ffffff); + } +}; + int main(int argc, char **argv) { + // The DEVBUS structure encapsulates wishbone accesses, so that this + // code can access the wishbone bus on the FPGA. + DEVBUS *m_fpga; + // Open up a port to talk to the FPGA ... + // + // This may be unique to your FPGA, so feel free to adjust these lines + // for your setup. The result, though, must be a DEVBUS structure + // giving you access to the FPGA. #ifndef FORCE_UART m_fpga = new FPGA(new NETCOMMS("lazarus",PORT)); #else @@ -82,75 +121,28 @@ m_fpga = new FPGA(new TTYCOMMS("/dev/ttyUSB2")); #endif - signal(SIGSTOP, closeup); - signal(SIGHUP, closeup); + // + CFGSCOPE *scope = new CFGSCOPE(m_fpga, WBSCOPE); // Check to see whether or not the scope has captured the data we need - // yet or not. If not, exit kindly. - unsigned v, lgln, scoplen; - v = m_fpga->readio(WBSCOPE); - if (0x60000000 != (v & 0x60000000)) { - printf("Scope is not yet ready:\n"); - printf("\tRESET:\t\t%s\n", (v&0x80000000)?"Ongoing":"Complete"); - printf("\tSTOPPED:\t%s\n", (v&0x40000000)?"Yes":"No"); - printf("\tTRIGGERED:\t%s\n", (v&0x20000000)?"Yes":"No"); - printf("\tPRIMED:\t\t%s\n", (v&0x10000000)?"Yes":"No"); - printf("\tMANUAL:\t\t%s\n", (v&0x08000000)?"Yes":"No"); - printf("\tDISABLED:\t%s\n", (v&0x04000000)?"Yes":"No"); - printf("\tZERO:\t\t%s\n", (v&0x02000000)?"Yes":"No"); - exit(0); - } + // yet or not. + if (scope->ready()) { + // If the data has been captured, we call print(). This + // function will print all our values to the standard output, + // and it will call the decode() function above to do it. + scope->print(); - // Since the length of the scope memory is a configuration parameter - // internal to the scope, we read it here to find out how it was - // configured. - lgln = (v>>20) & 0x1f; - scoplen = (1<readz(WBSCOPEDATA, scoplen, buf); - - printf("Vector read complete\n"); + // You can also write the results to a VCD trace file. To do + // this, just call writevcd and pass it the name you wish your + // VCD file to have. + scope->writevcd("cfgtrace.vcd"); } else { - for(int i=0; ireadio(WBSCOPEDATA); + // If the scope isnt yet ready, print a message, decode its + // current state, and exit kindly. + printf("Scope is not (yet) ready:\n"); + scope->decode_control(); } - // Now, let's decompose our 32-bit wires into something ... meaningful. - // This section will change from project to project, scope to scope, - // depending on what wires are placed into the scope. - for(int i=0; i0)&&(buf[i] == buf[i-1])&& - (i>24)&0x03f; - switch(cw) { - case 0x20: printf("DUMMY"); break; - case 0x10: printf("NOOP "); break; - case 0x08: printf("SYNC "); break; - case 0x04: printf("CMD "); break; - case 0x02: printf("IPROG"); break; - case 0x01: printf("DSYNC"); break; - default: printf("OTHER"); break; - } - printf(" -> %02x\n", buf[i] & 0x0ffffff); - } - // Clean up our interface, now, and we're done. delete m_fpga; } -
/trunk/sw/devbus.h
0,0 → 1,103
////////////////////////////////////////////////////////////////////////////////
//
// Filename: devbus.h
//
// Project: OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose: The purpose of this file is to document an interface which
// any devic with a bus, whether it be implemented over a UART,
// an ethernet, or a PCI express bus, must implement. This describes only
// an interface, and not how that interface is to be accomplished.
//
//
// 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.
//
// You should have received a copy of the GNU General Public License along
// 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.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#ifndef DEVBUS_H
#define DEVBUS_H
 
#include <stdio.h>
#include <unistd.h>
 
typedef unsigned int uint32;
 
class BUSERR {
public:
uint32 addr;
BUSERR(const uint32 a) : addr(a) {};
};
 
class DEVBUS {
public:
typedef uint32 BUSW;
 
virtual void kill(void) = 0;
virtual void close(void) = 0;
 
// Write a single value to a single address
virtual void writeio(const BUSW a, const BUSW v) = 0;
 
// Read a single value to a single address
virtual BUSW readio(const BUSW a) = 0;
 
// Read a series of values from values from a block of memory
virtual void readi(const BUSW a, const int len, BUSW *buf) = 0;
 
// Read a series of values from the same address in memory
virtual void readz(const BUSW a, const int len, BUSW *buf) = 0;
 
virtual void writei(const BUSW a, const int len, const BUSW *buf) = 0;
virtual void writez(const BUSW a, const int len, const BUSW *buf) = 0;
 
// Query whether or not an interrupt has taken place
virtual bool poll(void) = 0;
 
// Sleep until interrupt, but sleep no longer than msec milliseconds
virtual void usleep(unsigned msec) = 0;
 
// Sleep until an interrupt, no matter how long it takes for that
// interrupt to take place
virtual void wait(void) = 0;
 
// Query whether or not a bus error has taken place. This is somewhat
// of a misnomer, as my current bus error detection code exits any
// interface, but ... it is what it is.
virtual bool bus_err(void) const = 0;
 
// Clear any bus error condition.
virtual void reset_err(void) = 0;
 
// Clear any interrupt condition that has already been noticed by
// the interface, does not check for further interrupt
virtual void clear(void) = 0;
 
virtual ~DEVBUS(void) { };
};
 
#endif
/trunk/sw/scopecls.cpp
0,0 → 1,283
////////////////////////////////////////////////////////////////////////////////
//
// Filename: scopecls.cpp
//
// Project: WBScope, a wishbone hosted scope
//
// Purpose: After rebuilding the same code over and over again for every
// "scope" I tried to interact with, I thought it would be simpler
// to try to make a more generic interface, that other things could plug
// into. This is that more generic interface.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, 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.
//
// You should have received a copy of the GNU General Public License along
// 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.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <assert.h>
#include <time.h>
 
#include "devbus.h"
#include "scopecls.h"
 
bool SCOPE::ready() {
unsigned v;
v = m_fpga->readio(m_addr);
if (m_scoplen == 0) {
m_scoplen = (1<<((v>>20)&0x01f));
} v = (v>>28)&6;
return (v==6);
}
 
void SCOPE::decode_control(void) {
unsigned v;
 
v = m_fpga->readio(m_addr);
printf("\t31. RESET:\t%s\n", (v&0x80000000)?"Ongoing":"Complete");
printf("\t30. STOPPED:\t%s\n", (v&0x40000000)?"Yes":"No");
printf("\t29. TRIGGERED:\t%s\n", (v&0x20000000)?"Yes":"No");
printf("\t28. PRIMED:\t%s\n", (v&0x10000000)?"Yes":"No");
printf("\t27. MANUAL:\t%s\n", (v&0x08000000)?"Yes":"No");
printf("\t26. DISABLED:\t%s\n", (v&0x04000000)?"Yes":"No");
printf("\t25. ZERO:\t%s\n", (v&0x02000000)?"Yes":"No");
printf("\tSCOPLEN:\t%08x (%d)\n", m_scoplen, m_scoplen);
printf("\tHOLDOFF:\t%08x\n", (v&0x0fffff));
printf("\tTRIGLOC:\t%d\n", m_scoplen-(v&0x0fffff));
}
 
int SCOPE::scoplen(void) {
unsigned v, lgln;
 
// If the scope length is zero, then the scope isn't present.
// We use a length of zero here to also represent whether or not we've
// looked up the length by reading from the scope.
if (m_scoplen == 0) {
v = m_fpga->readio(m_addr);
 
// Since the length of the scope memory is a configuration
// parameter internal to the scope, we read it here to find
// out how the scope was configured.
lgln = (v>>20) & 0x1f;
 
// If the length is still zero, then there is no scope installed
if (lgln != 0) {
// Otherwise, the scope length contained in the device
// control register is the log base 2 of the actual
// length of what's in the FPGA. Here, we just convert
// that to the actual length of the scope.
m_scoplen = (1<<lgln);
}
// else we already know the length of the scope, and don't need to
// slow down to read that length from the device a second time.
} return m_scoplen;
}
 
//
// rawread
//
// Read the scope data from the scope.
void SCOPE::rawread(void) {
// If we've already read the data from the scope, then we don't need
// to read it a second time.
if (m_data)
return;
 
// Let's get the length of the scope, and check that it is a valid
// length
if (scoplen() <= 4) {
printf("ERR: Scope has less than a minimum length. Is it truly a scope?\n");
return;
}
 
// Now that we know the size of the scopes buffer, let's allocate a
// buffer to hold all this data
m_data = new DEVBUS::BUSW[m_scoplen];
 
// There are two means of reading from a DEVBUS interface: The first
// is a vector read, optimized so that the address and read command
// only needs to be sent once. This is the optimal means. However,
// if the bus isn't (yet) trustworthy, it may be more reliable to access
// the port by reading one register at a time--hence the second method.
// If the bus works, you'll want to use readz(): read scoplen values
// into the buffer, from the address WBSCOPEDATA, without incrementing
// the address each time (hence the 'z' in readz--for zero increment).
if (m_vector_read) {
m_fpga->readz(m_addr+4, m_scoplen, m_data);
} else {
for(unsigned int i=0; i<m_scoplen; i++)
m_data[i] = m_fpga->readio(m_addr+4);
}
}
 
void SCOPE::print(void) {
DEVBUS::BUSW addrv = 0;
 
rawread();
 
if(m_compressed) {
for(int i=0; i<(int)m_scoplen; i++) {
if ((m_data[i]>>31)&1) {
addrv += (m_data[i]&0x7fffffff);
printf(" ** (+0x%08x = %8d)\n",
(m_data[i]&0x07fffffff),
(m_data[i]&0x07fffffff));
continue;
}
printf("%10d %08x: ", addrv++, m_data[i]);
decode(m_data[i]);
printf("\n");
}
} else {
for(int i=0; i<(int)m_scoplen; i++) {
if ((i>0)&&(m_data[i] == m_data[i-1])&&(i<(int)(m_scoplen-1))) {
if ((i>2)&&(m_data[i] != m_data[i-2]))
printf(" **** ****\n");
continue;
} printf("%9d %08x: ", i, m_data[i]);
decode(m_data[i]);
printf("\n");
}
}
}
 
void SCOPE::write_trace_timescale(FILE *fp) {
fprintf(fp, "$timescle 1ns $end\n\n");
}
 
// $dumpoff and $dumpon
void SCOPE::write_trace_header(FILE *fp) {
time_t now;
 
time(&now);
fprintf(fp, "$version Generated by WBScope $end\n");
fprintf(fp, "$date %s\n $end\n", ctime(&now));
write_trace_timescale(fp);
 
fprintf(fp, " $scope module WBSCOPE $end\n");
// Print out all of the various values
fprintf(fp, " $var wire %2d \'C clk $end\n", 1);
fprintf(fp, " $var wire %2d \'R _raw_data [%d:0] $end\n",
(m_compressed)?31:32,
(m_compressed)?30:31);
 
for(unsigned i=0; i<m_traces.size(); i++) {
TRACEINFO *info = m_traces[i];
fprintf(fp, " $var wire %2d %s %s",
info->m_nbits, info->m_key, info->m_name);
if ((info->m_nbits > 0)&&(NULL == strchr(info->m_name, '[')))
fprintf(fp, "[%d:0] $end\n", info->m_nbits-1);
else
fprintf(fp, " $end\n");
}
 
fprintf(fp, " $upscope $end\n");
fprintf(fp, "$enddefinitions $end\n");
}
 
void SCOPE::write_binary_trace(FILE *fp, const int nbits, unsigned val,
const char *str) {
if (nbits <= 1) {
fprintf(fp, "%d%s\n", val&1, str);
return;
}
if ((unsigned)nbits < sizeof(val)*8)
val &= ~(-1<<nbits);
fputs("b", fp);
for(int i=0; i<nbits; i++)
fprintf(fp, "%d", (val>>(nbits-1-i))&1);
fprintf(fp, " %s\n", str);
}
 
void SCOPE::write_binary_trace(FILE *fp, TRACEINFO *info, unsigned value) {
write_binary_trace(fp, info->m_nbits, (value>>info->m_nshift),
info->m_key);
}
 
void SCOPE::register_trace(const char *name,
unsigned nbits, unsigned shift) {
TRACEINFO *info = new TRACEINFO;
int nkey = m_traces.size();
 
info->m_name = name;
info->m_nbits = nbits;
info->m_nshift = shift;
 
info->m_key[0] = 'v';
if (nkey < 26)
info->m_key[1] = 'a'+nkey;
else if (nkey < 26+26)
info->m_key[1] = 'A'+nkey-26;
else // if (nkey < 26+26+10) // Should never happen
info->m_key[1] = '0'+nkey-26-26;
info->m_key[2] = '\0';
info->m_key[3] = '\0';
 
m_traces.push_back(info);
}
 
void SCOPE::define_traces(void) {}
 
void SCOPE::writevcd(const char *trace_file_name) {
FILE *fp = fopen(trace_file_name, "w");
 
if (fp == NULL) {
fprintf(stderr, "ERR: Cannot open %s for writing!\n", trace_file_name);
fprintf(stderr, "ERR: Trace file not written\n");
return;
}
 
if (!m_data)
rawread();
 
write_trace_header(fp);
 
for(int i=0; i<(int)m_scoplen; i++) {
// Positive edge of the clock (everything is assumed to
// be on the positive edge)
fprintf(fp, "#%d\n", m_scoplen * 10);
fprintf(fp, "1\'C\n");
write_binary_trace(fp, (m_compressed)?31:32,
m_data[i], "\'R\n");
 
for(unsigned k=0; k<m_traces.size(); k++) {
TRACEINFO *info = m_traces[k];
write_binary_trace(fp, info, m_data[i]);
}
 
// Clock goes to zero
fprintf(fp, "#%d\n", m_scoplen * 10 + 5);
fprintf(fp, "0\'C\n");
}
}
 
/trunk/sw/scopecls.h
0,0 → 1,88
////////////////////////////////////////////////////////////////////////////////
//
// Filename: scopecls.h
//
// Project: WBScope, a wishbone hosted scope
//
// Purpose: After rebuilding the same code over and over again for every
// "scope" I tried to interact with, I thought it would be simpler
// to try to make a more generic interface, that other things could plug
// into. This is that more generic interface.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, 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.
//
// You should have received a copy of the GNU General Public License along
// 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.
//
// License: GPL, v3, as defined and found on www.gnu.org,
// http://www.gnu.org/licenses/gpl.html
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
#ifndef SCOPECLS_H
#define SCOPECLS_H
 
#include <vector>
#include "devbus.h"
 
class TRACEINFO {
public:
const char *m_name;
char m_key[4];
unsigned m_nbits, m_nshift;
};
 
class SCOPE {
DEVBUS *m_fpga;
DEVBUS::BUSW m_addr;
bool m_compressed, m_vector_read;
unsigned m_scoplen;
unsigned *m_data;
std::vector<TRACEINFO *> m_traces;
 
public:
SCOPE(DEVBUS *fpga, unsigned addr,
bool compressed=false, bool vecread=true)
: m_fpga(fpga), m_addr(addr),
m_compressed(compressed), m_vector_read(vecread),
m_scoplen(0), m_data(NULL) {}
~SCOPE(void) { if (m_data) delete[] m_data; }
 
bool ready();
void decode_control(void);
int scoplen(void);
virtual void rawread(void);
void print(void);
virtual void write_trace_timescale(FILE *fp);
virtual void write_trace_header(FILE *fp);
void write_binary_trace(FILE *fp, const int nbits,
unsigned val, const char *str);
void write_binary_trace(FILE *fp, TRACEINFO *info,
unsigned value);
void writevcd(const char *trace_file_name);
void register_trace(const char *varname,
unsigned nbits, unsigned shift);
virtual void decode(DEVBUS::BUSW v) const = 0;
virtual void define_traces(void);
};
 
#endif // SCOPECLS_H
/trunk/wbscope.core
0,0 → 1,15
CAPI=1
[main]
description = Pipelined Wishbone to AXI converter
 
[fileset rtl]
files =
rtl/wbscope.v
rtl/wbscopc.v
file_type = verilogSource
 
[provider]
name=github
user=ZipCPU
repo=wbscope
version = master
trunk Property changes : Added: svn:ignore ## -0,0 +1,2 ## +.git +.gitignore

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.