├── Doxyfile ├── Makefile ├── README ├── examples ├── basic.vhdl ├── board.vhdl ├── fb.vhdl ├── netpp.vhdl ├── pty.vhdl └── ram.vhdl ├── fifo.py ├── gensoc.mk ├── ghdlex.mk ├── ghdlsim.xml ├── h2vhdl.c ├── hdl ├── iomap_config.vhdl ├── libpipe.vhdl ├── libvirtual.vhdl ├── txt_util.vhdl ├── vbus.vhdl ├── vfifo.vhdl ├── vfx2fifo.vhdl ├── vram.vhdl ├── vram16.vhdl └── vram_dclk.vhdl ├── lib.mk ├── libfifo.vhdl ├── libnetpp.chdl ├── platform.mk ├── py ├── board.py ├── bus.py ├── fifo.py ├── ram.py ├── test.py ├── test_board.py ├── test_client.py └── utils.py ├── src ├── Makefile ├── apidef.h ├── apimacros.h ├── bus.c ├── bus.h ├── example.h ├── fifo.c ├── fifo.h ├── framebuf.c ├── ghpi.h ├── handler.c ├── helpers.c ├── main.c ├── netpp.c ├── netppwrap.h ├── pipe.c ├── project.mk ├── propbuild.c ├── ram.c ├── threadaux.c ├── threadaux.h ├── vpi_user.h ├── vpiwrapper.c └── vpiwrapper.h └── thread.c /Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.6.3 2 | 3 | #--------------------------------------------------------------------------- 4 | # Project related configuration options 5 | #--------------------------------------------------------------------------- 6 | DOXYFILE_ENCODING = UTF-8 7 | PROJECT_NAME = "GHDL extension library" 8 | PROJECT_NUMBER = 9 | OUTPUT_DIRECTORY = ./doc/ 10 | CREATE_SUBDIRS = NO 11 | OUTPUT_LANGUAGE = English 12 | BRIEF_MEMBER_DESC = YES 13 | REPEAT_BRIEF = NO 14 | ABBREVIATE_BRIEF = 15 | ALWAYS_DETAILED_SEC = YES 16 | INLINE_INHERITED_MEMB = YES 17 | FULL_PATH_NAMES = NO 18 | STRIP_FROM_PATH = 19 | STRIP_FROM_INC_PATH = 20 | SHORT_NAMES = NO 21 | JAVADOC_AUTOBRIEF = YES 22 | QT_AUTOBRIEF = NO 23 | MULTILINE_CPP_IS_BRIEF = NO 24 | INHERIT_DOCS = NO 25 | SEPARATE_MEMBER_PAGES = NO 26 | TAB_SIZE = 4 27 | ALIASES = 28 | OPTIMIZE_OUTPUT_FOR_C = NO 29 | OPTIMIZE_OUTPUT_JAVA = NO 30 | OPTIMIZE_FOR_FORTRAN = NO 31 | # OPTIMIZE_OUTPUT_VHDL = YES 32 | EXTENSION_MAPPING = 33 | BUILTIN_STL_SUPPORT = NO 34 | CPP_CLI_SUPPORT = NO 35 | SIP_SUPPORT = NO 36 | IDL_PROPERTY_SUPPORT = YES 37 | DISTRIBUTE_GROUP_DOC = NO 38 | SUBGROUPING = YES 39 | TYPEDEF_HIDES_STRUCT = NO 40 | SYMBOL_CACHE_SIZE = 0 41 | #--------------------------------------------------------------------------- 42 | # Build related configuration options 43 | #--------------------------------------------------------------------------- 44 | EXTRACT_ALL = NO 45 | EXTRACT_PRIVATE = NO 46 | EXTRACT_STATIC = NO 47 | EXTRACT_LOCAL_CLASSES = NO 48 | EXTRACT_LOCAL_METHODS = NO 49 | EXTRACT_ANON_NSPACES = NO 50 | HIDE_UNDOC_MEMBERS = YES 51 | HIDE_UNDOC_CLASSES = YES 52 | HIDE_FRIEND_COMPOUNDS = NO 53 | HIDE_IN_BODY_DOCS = NO 54 | INTERNAL_DOCS = NO 55 | CASE_SENSE_NAMES = YES 56 | HIDE_SCOPE_NAMES = YES 57 | SHOW_INCLUDE_FILES = YES 58 | FORCE_LOCAL_INCLUDES = NO 59 | INLINE_INFO = YES 60 | SORT_MEMBER_DOCS = YES 61 | SORT_BRIEF_DOCS = NO 62 | SORT_MEMBERS_CTORS_1ST = NO 63 | SORT_GROUP_NAMES = NO 64 | SORT_BY_SCOPE_NAME = NO 65 | GENERATE_TODOLIST = YES 66 | GENERATE_TESTLIST = YES 67 | GENERATE_BUGLIST = YES 68 | GENERATE_DEPRECATEDLIST= YES 69 | ENABLED_SECTIONS = 70 | MAX_INITIALIZER_LINES = 30 71 | SHOW_USED_FILES = YES 72 | SHOW_DIRECTORIES = NO 73 | SHOW_FILES = YES 74 | SHOW_NAMESPACES = YES 75 | FILE_VERSION_FILTER = 76 | LAYOUT_FILE = 77 | #--------------------------------------------------------------------------- 78 | # configuration options related to warning and progress messages 79 | #--------------------------------------------------------------------------- 80 | QUIET = NO 81 | WARNINGS = YES 82 | WARN_IF_UNDOCUMENTED = YES 83 | WARN_IF_DOC_ERROR = YES 84 | WARN_NO_PARAMDOC = NO 85 | WARN_FORMAT = "$file:$line: $text" 86 | WARN_LOGFILE = output.log 87 | #--------------------------------------------------------------------------- 88 | # configuration options related to the input files 89 | #--------------------------------------------------------------------------- 90 | INPUT = doc_apidef.h \ 91 | src/apimacros.h \ 92 | src/vpiwrapper.h \ 93 | src/fifo.h \ 94 | src/bus.h \ 95 | src/ghpi.h \ 96 | libnetpp.vhdl \ 97 | examples/netpp.vhdl \ 98 | examples/board.vhdl \ 99 | examples/pty.vhdl \ 100 | examples/fb.vhdl \ 101 | hdl/libvirtual.vhdl \ 102 | hdl/libpipe.vhdl \ 103 | hdl/vbus.vhdl \ 104 | hdl/vfifo.vhdl \ 105 | hdl/vfx2fifo.vhdl \ 106 | hdl/vram.vhdl \ 107 | hdl/vram_dclk.vhdl \ 108 | hdl/vram16.vhdl 109 | INPUT_ENCODING = UTF-8 110 | FILE_PATTERNS = 111 | RECURSIVE = NO 112 | EXCLUDE = 113 | EXCLUDE_SYMLINKS = NO 114 | EXCLUDE_PATTERNS = 115 | EXCLUDE_SYMBOLS = 116 | EXAMPLE_PATH = . examples 117 | EXAMPLE_PATTERNS = 118 | EXAMPLE_RECURSIVE = NO 119 | IMAGE_PATH = 120 | INPUT_FILTER = 121 | FILTER_PATTERNS = 122 | FILTER_SOURCE_FILES = NO 123 | #--------------------------------------------------------------------------- 124 | # configuration options related to source browsing 125 | #--------------------------------------------------------------------------- 126 | SOURCE_BROWSER = NO 127 | INLINE_SOURCES = NO 128 | STRIP_CODE_COMMENTS = YES 129 | REFERENCED_BY_RELATION = YES 130 | REFERENCES_RELATION = YES 131 | REFERENCES_LINK_SOURCE = YES 132 | USE_HTAGS = NO 133 | VERBATIM_HEADERS = NO 134 | #--------------------------------------------------------------------------- 135 | # configuration options related to the alphabetical class index 136 | #--------------------------------------------------------------------------- 137 | ALPHABETICAL_INDEX = NO 138 | COLS_IN_ALPHA_INDEX = 5 139 | IGNORE_PREFIX = 140 | #--------------------------------------------------------------------------- 141 | # configuration options related to the HTML output 142 | #--------------------------------------------------------------------------- 143 | GENERATE_HTML = YES 144 | HTML_OUTPUT = html/ 145 | HTML_FILE_EXTENSION = .html 146 | HTML_HEADER = 147 | HTML_FOOTER = 148 | HTML_STYLESHEET = 149 | HTML_TIMESTAMP = YES 150 | HTML_ALIGN_MEMBERS = YES 151 | HTML_DYNAMIC_SECTIONS = NO 152 | GENERATE_DOCSET = NO 153 | DOCSET_FEEDNAME = "Doxygen generated docs" 154 | DOCSET_BUNDLE_ID = org.doxygen.Project 155 | GENERATE_HTMLHELP = NO 156 | CHM_FILE = ghdlex.chm 157 | HHC_LOCATION = index.hhc 158 | GENERATE_CHI = YES 159 | CHM_INDEX_ENCODING = 160 | BINARY_TOC = YES 161 | TOC_EXPAND = YES 162 | GENERATE_QHP = NO 163 | QCH_FILE = 164 | QHP_NAMESPACE = org.doxygen.Project 165 | QHP_VIRTUAL_FOLDER = doc 166 | QHP_CUST_FILTER_NAME = 167 | QHP_CUST_FILTER_ATTRS = 168 | QHP_SECT_FILTER_ATTRS = 169 | QHG_LOCATION = 170 | GENERATE_ECLIPSEHELP = NO 171 | ECLIPSE_DOC_ID = org.doxygen.Project 172 | DISABLE_INDEX = NO 173 | ENUM_VALUES_PER_LINE = 4 174 | GENERATE_TREEVIEW = NO 175 | USE_INLINE_TREES = NO 176 | TREEVIEW_WIDTH = 250 177 | FORMULA_FONTSIZE = 10 178 | SEARCHENGINE = NO 179 | SERVER_BASED_SEARCH = NO 180 | #--------------------------------------------------------------------------- 181 | # configuration options related to the LaTeX output 182 | #--------------------------------------------------------------------------- 183 | GENERATE_LATEX = NO 184 | LATEX_OUTPUT = latex/ 185 | LATEX_CMD_NAME = latex 186 | MAKEINDEX_CMD_NAME = makeindex 187 | COMPACT_LATEX = NO 188 | PAPER_TYPE = a4wide 189 | EXTRA_PACKAGES = 190 | LATEX_HEADER = 191 | PDF_HYPERLINKS = YES 192 | USE_PDFLATEX = YES 193 | LATEX_BATCHMODE = NO 194 | LATEX_HIDE_INDICES = NO 195 | LATEX_SOURCE_CODE = NO 196 | #--------------------------------------------------------------------------- 197 | # configuration options related to the RTF output 198 | #--------------------------------------------------------------------------- 199 | GENERATE_RTF = NO 200 | RTF_OUTPUT = rtf 201 | COMPACT_RTF = NO 202 | RTF_HYPERLINKS = YES 203 | RTF_STYLESHEET_FILE = 204 | RTF_EXTENSIONS_FILE = 205 | #--------------------------------------------------------------------------- 206 | # configuration options related to the man page output 207 | #--------------------------------------------------------------------------- 208 | GENERATE_MAN = NO 209 | MAN_OUTPUT = man 210 | MAN_EXTENSION = .3 211 | MAN_LINKS = NO 212 | #--------------------------------------------------------------------------- 213 | # configuration options related to the XML output 214 | #--------------------------------------------------------------------------- 215 | GENERATE_XML = NO 216 | XML_OUTPUT = xml 217 | XML_SCHEMA = 218 | XML_DTD = 219 | XML_PROGRAMLISTING = YES 220 | #--------------------------------------------------------------------------- 221 | # configuration options for the AutoGen Definitions output 222 | #--------------------------------------------------------------------------- 223 | GENERATE_AUTOGEN_DEF = NO 224 | #--------------------------------------------------------------------------- 225 | # configuration options related to the Perl module output 226 | #--------------------------------------------------------------------------- 227 | GENERATE_PERLMOD = NO 228 | PERLMOD_LATEX = NO 229 | PERLMOD_PRETTY = YES 230 | PERLMOD_MAKEVAR_PREFIX = 231 | #--------------------------------------------------------------------------- 232 | # Configuration options related to the preprocessor 233 | #--------------------------------------------------------------------------- 234 | ENABLE_PREPROCESSING = YES 235 | MACRO_EXPANSION = YES 236 | EXPAND_ONLY_PREDEF = NO 237 | SEARCH_INCLUDES = NO 238 | INCLUDE_PATH = src 239 | INCLUDE_FILE_PATTERNS = 240 | PREDEFINED = RUN_CHEAD 241 | EXPAND_AS_DEFINED = 242 | SKIP_FUNCTION_MACROS = YES 243 | #--------------------------------------------------------------------------- 244 | # Configuration::additions related to external references 245 | #--------------------------------------------------------------------------- 246 | TAGFILES = 247 | GENERATE_TAGFILE = 248 | ALLEXTERNALS = NO 249 | EXTERNAL_GROUPS = YES 250 | PERL_PATH = /usr/bin/perl 251 | #--------------------------------------------------------------------------- 252 | # Configuration options related to the dot tool 253 | #--------------------------------------------------------------------------- 254 | CLASS_DIAGRAMS = YES 255 | MSCGEN_PATH = 256 | HIDE_UNDOC_RELATIONS = YES 257 | HAVE_DOT = NO 258 | DOT_FONTNAME = FreeSans 259 | DOT_FONTSIZE = 10 260 | DOT_FONTPATH = 261 | CLASS_GRAPH = YES 262 | COLLABORATION_GRAPH = YES 263 | GROUP_GRAPHS = YES 264 | UML_LOOK = NO 265 | TEMPLATE_RELATIONS = YES 266 | INCLUDE_GRAPH = YES 267 | INCLUDED_BY_GRAPH = YES 268 | CALL_GRAPH = NO 269 | CALLER_GRAPH = NO 270 | GRAPHICAL_HIERARCHY = YES 271 | DIRECTORY_GRAPH = YES 272 | DOT_IMAGE_FORMAT = png 273 | DOT_PATH = 274 | DOTFILE_DIRS = 275 | DOT_GRAPH_MAX_NODES = 50 276 | MAX_DOT_GRAPH_DEPTH = 0 277 | DOT_TRANSPARENT = NO 278 | DOT_MULTI_TARGETS = YES 279 | GENERATE_LEGEND = YES 280 | DOT_CLEANUP = YES 281 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for GHDL threading simulator interface 2 | # 3 | # (c) 2011-2014 Martin Strubel 4 | # 5 | # See LICENSE.txt for usage policies 6 | # 7 | 8 | VERSION = 0.2 9 | 10 | DEVICEFILE = ghdlsim.xml 11 | 12 | GHDLEX = $(CURDIR) 13 | 14 | ifdef DEVELOP 15 | GHDLDIR = /data/src/ghdl/translate 16 | GHDL = $(GHDLDIR)/ghdldrv/ghdl_gcc 17 | GHDL_LIBPREFIX = $(GHDLDIR)/lib 18 | else 19 | GHDL ?= ghdl 20 | endif 21 | 22 | include platform.mk 23 | -include config.mk 24 | 25 | LIBDIR ?= ghdl 26 | 27 | # USE_LEGACY = yes 28 | 29 | 30 | # Not really needed for "sane" VHDL code. Use only for deprecated 31 | # VDHL code. Some Xilinx simulation libraries might need it. 32 | # 33 | # GHDLFLAGS = --ieee=synopsys 34 | 35 | HOST_CC ?= gcc 36 | 37 | GHDLFLAGS = --workdir=$(LIBDIR) -P$(LIBDIR) 38 | 39 | GHDL_LDFLAGS += $(GHDLFLAGS) 40 | 41 | # Set to NETPP location, if you want to use specific NETPP dir 42 | # It is better to put this into config.mk: 43 | # NETPP = $(HOME)/src/netpp 44 | # 45 | NETPP ?= /usr/share/netpp 46 | GENSOC ?= $(shell which gensoc) 47 | 48 | CONFIG_NETPP = $(shell [ -e $(NETPP)/xml ] && echo y ) 49 | CONFIG_GENSOC = $(shell [ -e $(GENSOC) ] && echo y ) 50 | CURDIR = $(shell pwd) 51 | 52 | WORK = $(LIBDIR)/work-obj93.cf 53 | 54 | ifeq ($(CONFIG_NETPP),y) 55 | VARIANT = -netpp 56 | CFLAGS += -DCONFIG_NETPP 57 | endif 58 | 59 | 60 | LIBGHDLEX = libghdlex$(VARIANT) 61 | 62 | LIBRARIES = $(LIBGHDLEX)$(DLLEXT) $(LIBDIR)/ghdlex-obj93.cf 63 | 64 | $(LIBGHDLEX)$(DLLEXT): 65 | $(MAKE) -C src NETPP=$(NETPP) LIBSIM=$(LIBGHDLEX) 66 | 67 | HOST_CFLAGS = -g -Wall -Isrc 68 | 69 | NO_CLEANUP_DUTIES-y = 70 | NO_CLEANUP_DUTIES-$(CONFIG_NETPP) += $(NETPP)/common 71 | 72 | DUTIES-y = 73 | DUTIES-$(CONFIG_NETPP) += simnetpp 74 | DUTIES-$(CONFIG_NETPP_DISPLAY) += simfb 75 | DUTIES-$(CONFIG_NETPP) += simram simboard 76 | 77 | DUTIES-$(CONFIG_LINUX) += simpty 78 | 79 | DUTIES = $(DUTIES-y) 80 | 81 | LIBSLAVE = $(NETPP)/devices/libslave 82 | 83 | GHDLEX_VHDL = $(wildcard hdl/*.vhdl) 84 | 85 | ifdef USE_LEGACY 86 | GHDLEX_VHDL += libfifo.vhdl 87 | VHDLFILES = examples/fifo.vhdl 88 | endif 89 | 90 | SOC_MODULES = localbus netppbus 91 | 92 | SOC_VHDL = $(SOC_MODULES:%=ghdlex_%_decode.vhdl) ghdlex_iomap_pkg.vhdl 93 | 94 | 95 | -include gensoc.mk 96 | 97 | CONVERTED_VHDL-$(CONFIG_NETPP)+= libnetpp.vhdl 98 | VHDLFILES-$(CONFIG_NETPP) += examples/netpp.vhdl 99 | VHDLFILES-$(CONFIG_NETPP_DISPLAY) += examples/fb.vhdl 100 | 101 | VHDLFILES-$(CONFIG_NETPP) += examples/ram.vhdl 102 | VHDLFILES-$(CONFIG_NETPP) += examples/board.vhdl 103 | 104 | 105 | VHDLFILES += $(VHDLFILES-y) 106 | 107 | # Pipe example obsolete, see improved pty.vhdl 108 | VHDLFILES += examples/pty.vhdl 109 | 110 | GHDLEX_VHDL += $(CONVERTED_VHDL-y) 111 | GHDLEX_VHDL += $(GENERATED_VHDL-y) 112 | 113 | 114 | all: $(NO_CLEANUP_DUTIES-y) $(DUTIES) 115 | 116 | 117 | DISTFILES = $(FILES:%=ghdlex/%) 118 | 119 | SRCDISTFILES = $(DISTFILES) $(SRCFILES:%=ghdlex/%) 120 | 121 | srcdist: $(GHDLEX_VHDL) 122 | cd ..; \ 123 | tar cfz ghdlex-src-$(VERSION).tgz $(SRCDISTFILES) 124 | 125 | dist: 126 | cd ..; \ 127 | tar cfz ghdlex-sim-$(VERSION).tgz $(DISTFILES) 128 | 129 | 130 | ifeq ($(CONFIG_NETPP),y) 131 | include $(NETPP)/xml/prophandler.mk 132 | else 133 | endif 134 | 135 | libnetpp.vhdl: libnetpp.chdl func_decl.chdl func_body.chdl 136 | cpp -P -o $@ $< 137 | 138 | func_decl.chdl func_body.chdl: h2vhdl 139 | ./$< func 140 | 141 | 142 | $(WORK): $(VHDLFILES) $(LIBDIR)/ghdlex-obj93.cf 143 | [ -e $(dir $@) ] || mkdir $(dir $@) 144 | $(GHDL) -i $(GHDLFLAGS) --work=work $(VHDLFILES) 145 | 146 | show: 147 | @echo $(LIBDIR)/ghdlex-obj93.cf 148 | @echo Has gensoc: $(CONFIG_GENSOC) 149 | @echo Has netpp: $(CONFIG_NETPP) 150 | @echo $(VHDLFILES) 151 | 152 | 153 | GHDLEX_LDFLAGS = -Wl,-Lsrc -Wl,-no-pie -Wl,-lghdlex$(VARIANT) 154 | 155 | LDFLAGS += $(GHDLEX_LDFLAGS) 156 | 157 | ifeq ($(CONFIG_NETPP),y) 158 | LDFLAGS-$(CONFIG_LINUX) += -Wl,-L$(LIBSLAVE) -Wl,-lslave 159 | LDFLAGS-$(CONFIG_MINGW32) += -Wl,-L$(LIBSLAVE)/$(CROSS)-Debug -Wl,-lslave 160 | endif 161 | # Make sure libslave can use local_getroot(): 162 | LDFLAGS-$(CONFIG_LINUX) += -Wl,-lpthread -Wl,-export-dynamic 163 | LDFLAGS-$(CONFIG_MINGW32) += -Wl,-lws2_32 164 | 165 | LDFLAGS += $(LDFLAGS-y) 166 | 167 | # Rule to build simulation examples: 168 | sim%: $(WORK) $(LIBRARIES) src/main.o 169 | $(GHDL) -m $(GHDL_LDFLAGS) $(LDFLAGS) $@ 170 | $(GHDL) --bind $(GHDL_LDFLAGS) $(LDFLAGS) $@ 171 | LINK_OBJS=`$(GHDL) --list-link $(GHDL_LDFLAGS) $(LDFLAGS) $@`; \ 172 | $(CC) -o $@ src/main.o $$LINK_OBJS 173 | 174 | # src/libghdlex-master.a: 175 | # $(MAKE) -C src NETPP=$(NETPP) LIBSIM=libghdlex-master libghdlex-master.a 176 | # 177 | # simnetpp: $(WORK) src/libghdlex-master.a 178 | # $(GHDL) -m $(GHDLFLAGS) \ 179 | # -Wl,-Lsrc -Wl,-lghdlex-master \ 180 | # -Wl,-L$(NETPP)/devices/libmaster/Debug -Wl,-lmaster \ 181 | # -Wl,-lpthread \ 182 | # $@ 183 | 184 | # The ghdlex library for external use: 185 | $(LIBDIR)/ghdlex-obj93.cf: $(GHDLEX_VHDL) 186 | [ -e $(LIBDIR) ] || mkdir $(LIBDIR) 187 | export GHDL_PREFIX=$(GHDL_LIBPREFIX); \ 188 | $(GHDL) -i --work=ghdlex --workdir=$(LIBDIR) $(GHDLEX_VHDL) 189 | 190 | clean:: clean_duties 191 | rm -fr $(LIBDIR) 192 | rm -f $(GENERATED_VHDL-y) 193 | rm -f $(WORK) 194 | $(MAKE) -C src clean LIBSIM=$(LIBGHDLEX) 195 | 196 | clean_duties: 197 | rm -f $(DUTIES) h2vhdl 198 | rm -f func_decl.chdl func_body.chdl 199 | 200 | FILES = LICENSE.txt README Makefile 201 | # No more support for VPI stuff: 202 | # FILES += src/vpi_user.h 203 | FILES = $(GHDLEX_VHDL) $(VHDLFILES) 204 | SRCFILES += src/fifo.h src/ghpi.h src/netppwrap.h src/example.h 205 | SRCFILES += src/bus.h 206 | SRCFILES += ghdlsim.xml py/test.py 207 | 208 | FILES += libnetpp.chdl h2vhdl.c 209 | 210 | FILES += lib.mk platform.mk ghdlex.mk 211 | 212 | SRCFILES += $(CSRCS) apidef.h apimacros.h threadaux.h registermap.h 213 | SRCFILES += vpi_user.h vpiwrapper.c 214 | SRCFILES += src/Makefile Doxyfile 215 | 216 | $(NETPP)/common: 217 | ln -s $< $(NETPP)/share/netpp/common $@ 218 | 219 | thread.o: thread.c 220 | $(CC) -o $@ -c $(CFLAGS) $< 221 | 222 | 223 | doc_apidef.h: src/apidef.h 224 | cpp -C -E -DRUN_CHEAD -DNO_MACRO_DOCS $< >$@ 225 | 226 | docs: doc_apidef.h libnetpp.vhdl Doxyfile 227 | doxygen 228 | 229 | h2vhdl.o: h2vhdl.c src/apidef.h 230 | $(HOST_CC) -o $@ $(HOST_CFLAGS) -c $< 231 | 232 | h2vhdl: h2vhdl.o 233 | $(HOST_CC) -o $@ $< 234 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | ghdlex opensource distribution 2 | ---------------------------------------------------------------------------- 3 | 4 | This demonstrates usage of threading to remote control a VHDL simulation 5 | from a C program (or Python via netpp, optionally). 6 | This library can be used to deploy networked swarms of independent 7 | simulation test bench executables generated by GHDL 8 | (https://github.com/ghdl/ghdl) 9 | 10 | ---------------------------------------------------------------------------- 11 | QUICK DEPLOYMENT 12 | ---------------- 13 | 14 | For the impatient, there is a docker container with all necessary tools 15 | preinstalled. 16 | 17 | > docker run -it -p 7208:7208/udp -p 2010:2010/tcp \ 18 | hackfin/masocist 19 | 20 | .. go for a coffee .. 21 | 22 | Inside the container: 23 | 24 | $ git clone https://github.com/hackfin/ghdlex.git 25 | $ cd ghdlex; make all 26 | 27 | Once the simulation examples were built (try `ls sim*` to verify), 28 | you can run them after you've set the LD_LIBRARY_PATH: 29 | 30 | $ export LD_LIBRARY_PATH=`pwd`/src 31 | 32 | $ ./simboard 33 | 34 | 35 | ----- 36 | 37 | Listening on UDP Port 2010... 38 | Listening on TCP Port 2010... 39 | Reserved RAM ':simboard:ram0:' with word size 0x1000(8192 bytes), width: 16 bits 40 | Registered RAM property with name ':simboard:ram0:' 41 | Reserved RAM ':simboard:ram1:' with word size 0x1000(8192 bytes), width: 16 bits 42 | ... 43 | 44 | ----- 45 | 46 | Inside the container, you can use the 'netpp' command to access the simulation. 47 | From the outside, the '-p' flags from above come into play for port forwarding. 48 | 49 | If running on a Windows host OS, you will have to install the netpp client 50 | according to 51 | 52 | https://section5.ch/index.php/2017/09/12/netpp-for-windows-quick-start/ 53 | 54 | 55 | ---------------------------------------------------------------------------- 56 | 57 | List of example simulations: 58 | 59 | 60 | * simpty: Virtual PTY. See examples/pty.vhdl on how to initialize a 61 | a PTY using socat. This example can be used to build a true 62 | virtual UART. 63 | 64 | These are netpp featured and will not be compiled by default (unless you 65 | have netpp installed and pass the installation directory to 'make' using 66 | the NETPP variable or an extra custom 'config.mk'. 67 | 68 | * simnetpp: A client example. Connects to the netpp example device (see 69 | devices/example/slave) and modifies the "ControlReg" property remotely. 70 | 71 | * simfb: Connects to the remote 'display' server and shows a 256x256 72 | YUV color test image. 73 | 74 | * simboard: Simulates a virtual board with various virtual I/O's, 75 | virtual RAM, FIFOs, etc. 76 | 77 | To compile the example, run 'make'. You will end up with a simulation 78 | executable 'simboard' plus a src/libghdlex.so or src/libghdlex-netpp.so DLL. 79 | 80 | To run the simulation, you have to add the path to this DLL to the 81 | LD_LIBRARY_PATH, for instance: 82 | 83 | > export LD_LIBRARY_PATH=`pwd`/src:$LD_LIBRARY_PATH 84 | 85 | Then you can run the simulation: 86 | 87 | > ./simboard --stop-time=500ms --wave=test.ghw 88 | 89 | Use gtkwave to look at the traces: 90 | 91 | > gtkwave test.ghw 92 | 93 | To build the netpp featured variants, run 94 | 95 | > make allnetpp 96 | 97 | and wait for things to finish. You may have to run a "make clean" before that. 98 | 99 | Cross compiling GHDLEX is only supported for mingw32. Run 100 | 101 | > make MINGW32=1 102 | 103 | to compile a library for the i686-w64-mingw32 cross compiler. 104 | 105 | ---------------------------------------------------------------------------- 106 | NETPP FEATURES 107 | 108 | To test if your simulation is accessible through netpp, try this: 109 | 110 | 1) Start an example simulation, like simboard: 111 | 112 | > ./simboard 113 | 114 | 2) Run the netpp client 'master': 115 | 116 | > ./netpp/master/master TCP:localhost:2010 117 | 118 | This will output the properties of your virtual board: 119 | 120 | Properties of Device 'VirtualBoard' associated with Hub 'TCP': 121 | Child: [80000001] ':simboard:ram0:' 122 | Child: [80000002] ':simboard:ram1:' 123 | ... 124 | 125 | 126 | Likewise, you could mess with the simple FIFO loopback test implemented 127 | in examples/board.vhdl: 128 | 129 | > python py/board.py 130 | 131 | This shows how the simulation is (remotely) accessed by a python script. 132 | 133 | ---------------------------------------------------------------------------- 134 | LIBRARY OPTIONS 135 | 136 | As you might have figured out, there are two ways to enhance your VHDL 137 | simulation: 138 | 1) By just adding the --vpi=netpp.vpi flag: This quickly exports the top level 139 | signals to netpp without you requiring to explicitely register anything. 140 | 141 | 2) By explicitely initializing NETPP from your simulation. See doxygen 142 | documentation. 143 | 144 | 145 | 146 | ---------------------------------------------------------------------------- 147 | EXPERIMENTAL 148 | 149 | GHDL provides another way of extension via the VPI interface. This allows 150 | to export pin names and manipulate signals by external DLLs that are loaded 151 | dynamically. The netpp.vpi module gathers all signals from the top module 152 | interface or virtual hardware modules and exports them to a netpp server, 153 | meaning, they can be accessed and manipulated from outside via a netpp 154 | client. This is based on a netpp version >=0.40 with support for dynamic 155 | properties. 156 | Since this approach makes use of multiple threads, it may not be 100% safe 157 | for signal manipulation. This way of signal manipulation is also limited, 158 | and not timing-accurate. 159 | 160 | ---------------------------------------------------------------------------- 161 | 162 | More details about netpp: 163 | http://www.section5.ch/netpp 164 | 165 | For all other details: Read the source, Luke. 166 | 167 | For usage information, see LICENSE.txt 168 | 169 | 170 | Have fun! 171 | 172 | ---------------------------------------------------------------------------- 173 | 2011-2014, 174 | -------------------------------------------------------------------------------- /examples/basic.vhdl: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.numeric_std.all; -- Unsigned 4 | 5 | entity simple is end simple; 6 | architecture behaviour of simple is 7 | signal clk : std_logic := '0'; 8 | signal sigterm : std_logic := '0'; 9 | signal counter : unsigned(7 downto 0) := x"00"; 10 | begin 11 | process 12 | begin 13 | wait for 5 us; 14 | clkloop : loop 15 | wait for 1 us; 16 | clk <= not clk; 17 | if sigterm = '1' then 18 | exit; 19 | end if; 20 | end loop clkloop; 21 | wait for 5 us; 22 | wait; 23 | end process; 24 | 25 | process (clk) 26 | begin 27 | if rising_edge(clk) then 28 | if counter = 16 then 29 | sigterm <= '1'; 30 | end if; 31 | counter <= counter + 1; 32 | end if; 33 | end process; 34 | end behaviour; 35 | -------------------------------------------------------------------------------- /examples/board.vhdl: -------------------------------------------------------------------------------- 1 | --! \file board.vhdl Virtual board example 2 | -- A little more complex configuration 3 | -- 4 | -- Test for various virtual entities 5 | 6 | library ieee; 7 | use ieee.std_logic_1164.all; 8 | use ieee.numeric_std.all; -- Unsigned 9 | 10 | library ghdlex; 11 | use ghdlex.ghpi_netpp.all; 12 | use ghdlex.virtual.all; 13 | use ghdlex.txt_util.all; 14 | use ghdlex.ghdlsim.all; 15 | 16 | use std.textio.all; 17 | 18 | entity simboard is 19 | end simboard; 20 | 21 | architecture simulation of simboard is 22 | constant FIFO_WORDWIDTH : natural := 1; 23 | constant VBUS_ADDR_W : natural := 12; -- Maximum address bus width 24 | 25 | constant initialization : integer := netpp_init("VirtualBoard", 2010); 26 | 27 | signal global_startup_reset: std_logic := '1'; 28 | 29 | signal clk: std_logic := '0'; 30 | signal we: std_logic := '0'; 31 | signal dclk: std_logic := '0'; 32 | signal dwe: std_logic := '0'; 33 | constant ADDR_W : natural := 12; 34 | signal addr: unsigned(ADDR_W-1 downto 0) := (others => '0'); 35 | signal data0: unsigned(31 downto 0); 36 | signal data1: unsigned(15 downto 0); 37 | signal data2: unsigned(15 downto 0); 38 | 39 | signal addr_a: unsigned(ADDR_W-1 downto 0) := (others => '0'); 40 | signal addr_b: unsigned(ADDR_W-1 downto 0) := (others => '0'); 41 | signal data_a: unsigned(15 downto 0) := x"0100"; 42 | signal data_b: unsigned(15 downto 0); 43 | 44 | -- Virtual global (netpp) bus: 45 | signal vbus_wr : std_logic; 46 | signal vbus_rd : std_logic; 47 | signal vbus_din : std_logic_vector(31 downto 0) := (others => '0'); 48 | signal vbus_dout : std_logic_vector(31 downto 0) := (others => '0'); 49 | signal vbus_addr : std_logic_vector(VBUS_ADDR_W-1 downto 0) 50 | := (others => '0'); 51 | 52 | 53 | -- Virtual local bus, without netpp device read/write access: 54 | signal lbus_wr : std_logic; 55 | signal lbus_rd : std_logic; 56 | signal lbus_din : std_logic_vector(31 downto 0) := (others => '0'); 57 | signal lbus_dout : std_logic_vector(31 downto 0) := (others => '0'); 58 | signal lbus_addr : std_logic_vector(ADDR_W-1 downto 0) 59 | := (others => '0'); 60 | 61 | 62 | signal lbus_ce : std_logic; 63 | signal lbus_ctrl : localbus_WritePort; 64 | signal lbus_stat : localbus_ReadPort; 65 | 66 | -- netpp Test access port: 67 | signal vtap_ce : std_logic; 68 | signal vtap_ctrl : netppbus_WritePort; 69 | signal vtap_stat : netppbus_ReadPort; 70 | 71 | constant NFIFOS : natural := 2; 72 | 73 | type byte_bus_t is array (0 to NFIFOS-1) of 74 | std_logic_vector(FIFO_WORDWIDTH*8-1 downto 0); 75 | 76 | signal fifo_canwrite : std_logic; 77 | signal fifo_wready : std_logic_vector(0 to NFIFOS-1); 78 | signal fifo_rready : std_logic_vector(0 to NFIFOS-1); 79 | signal fifo_we : std_logic_vector(0 to NFIFOS-1); 80 | signal fifo_re : std_logic_vector(0 to NFIFOS-1); 81 | -- Note: Arrays of that kind are not yet supported by the wrapper 82 | signal fifo_din : byte_bus_t; 83 | signal fifo_dout : byte_bus_t; 84 | begin 85 | 86 | clkgen: 87 | process 88 | begin 89 | clkloop : loop 90 | wait for 10 us; 91 | clk <= not clk; 92 | -- if sigterm = '1' then 93 | -- exit; 94 | -- end if; 95 | end loop clkloop; 96 | print(output, " -- TERMINATED --"); 97 | 98 | end process; 99 | 100 | 101 | dclk <= not dclk after 5 us; 102 | 103 | -- Legacy 16 bit RAM: 104 | ram0: 105 | DualPort16 generic map (ADDR_W => ADDR_W) 106 | port map ( 107 | clk => clk, 108 | -- Port A 109 | a_we => we, 110 | a_addr => addr, 111 | a_write => data0(15 downto 0), 112 | a_read => data1, 113 | -- Port B 114 | b_we => '0', 115 | b_addr => addr, 116 | b_write => data0(15 downto 0), 117 | b_read => open 118 | ); 119 | 120 | ram1: 121 | VirtualDualPortRAM generic map (ADDR_W => ADDR_W, DATA_W => 16) 122 | port map ( 123 | clk => clk, 124 | -- Port A 125 | a_we => we, 126 | a_addr => addr, 127 | a_write => data0(31 downto 16), 128 | a_read => data2, 129 | -- Port B 130 | b_we => '0', 131 | b_addr => addr, 132 | b_write => data0(31 downto 16), 133 | b_read => open 134 | ); 135 | 136 | ram_dc: 137 | VirtualDualPortRAM_dc generic map (ADDR_W => ADDR_W, DATA_W => 16) 138 | port map ( 139 | -- Port A 140 | a_clk => dclk, 141 | a_we => dwe, 142 | a_addr => addr_a, 143 | a_write => data_a, 144 | a_read => open, 145 | -- Port B 146 | b_clk => clk, 147 | b_we => '0', 148 | b_addr => addr_b, 149 | b_write => x"0000", 150 | b_read => data_b 151 | ); 152 | ram32: 153 | VirtualDualPortRAM generic map ( NETPP_NAME => "Shadow32bit", 154 | ADDR_W => ADDR_W, DATA_W => 32) 155 | port map ( 156 | clk => clk, 157 | -- Port A 158 | a_we => we, 159 | a_addr => addr, 160 | a_write => data0, 161 | a_read => open, 162 | -- Port B 163 | b_we => '0', 164 | b_addr => addr, 165 | b_write => data0, 166 | b_read => open 167 | ); 168 | 169 | -- Create a FIFO loopback: 170 | 171 | nfifo: 172 | for i in 0 to 1 generate 173 | fifo: VirtualFIFO 174 | generic map (WORDSIZE => FIFO_WORDWIDTH) 175 | port map ( 176 | clk => clk, 177 | throttle => global_throttle, 178 | wr_ready => fifo_wready(i), 179 | rd_ready => fifo_rready(i), 180 | wr_enable => fifo_we(i), 181 | rd_enable => fifo_re(i), 182 | data_in => fifo_dout(i), 183 | data_out => fifo_din(i) 184 | ); 185 | end generate; 186 | 187 | -- Feed out data to input: 188 | 189 | fifo_din(0) <= fifo_dout(0); 190 | 191 | -- Delayed process, because full flag signals on (FULL-1) pointer 192 | -- condition: 193 | loopback: 194 | process (clk) 195 | begin 196 | if rising_edge(clk) then 197 | fifo_re(0) <= fifo_rready(0); -- Read when data ready 198 | fifo_canwrite <= fifo_wready(0); 199 | fifo_we(0) <= fifo_rready(0) and fifo_canwrite; 200 | end if; 201 | end process; 202 | 203 | -- Disabled, we play with the procedural generation above. 204 | 205 | -- fifo_single: VirtualFIFO 206 | -- generic map (WORDSIZE => 1) 207 | -- port map ( 208 | -- clk => clk, 209 | -- throttle => global_throttle, 210 | -- wr_ready => fifo_wready(2), 211 | -- rd_ready => fifo_rready(2), 212 | -- wr_enable => fifo_we(2), 213 | -- rd_enable => fifo_re(2), 214 | -- data_in => fifo_dout(2), 215 | -- data_out => fifo_din(2) 216 | -- ); 217 | 218 | -- Local bus: This one does is local, i.e. the netpp device layer 219 | -- only allows direct raw access through the 'localbus' property. 220 | virtual_local_bus: 221 | VirtualBus 222 | generic map ( ADDR_W => ADDR_W, BUSTYPE => BUS_LOCAL, NETPP_NAME => "localbus" ) 223 | port map ( 224 | clk => clk, 225 | wr => lbus_wr, 226 | rd => lbus_rd, 227 | wr_busy => '0', 228 | rd_busy => '0', 229 | addr => lbus_addr, 230 | data_in => lbus_din, 231 | data_out => lbus_dout 232 | ); 233 | 234 | lbus_ce <= lbus_wr or lbus_rd; 235 | 236 | lbus_stat.magicid <= x"baadf00d"; 237 | lbus_stat.magic2 <= x"facebead"; 238 | lbus_stat.fwrev_maj <= std_logic_vector(to_unsigned(HWREV_ghdlsim_MAJOR, 4)); 239 | lbus_stat.fwrev_min <= std_logic_vector(to_unsigned(HWREV_ghdlsim_MINOR, 4)); 240 | 241 | -- This instances the bus decoders for the netpp accessible local and 242 | -- global property bus. 243 | 244 | local_decoder: 245 | entity ghdlex.decode_localbus 246 | generic map (DATA_WIDTH => 32) 247 | port map ( 248 | ce => lbus_ce, 249 | reset => vtap_ctrl.reset, 250 | ctrl => lbus_ctrl, 251 | stat => lbus_stat, 252 | data_in => lbus_din, 253 | data_out => lbus_dout, 254 | addr => lbus_addr(BV_MMR_CFG_localbus), 255 | we => lbus_wr, 256 | re => lbus_rd, 257 | clk => clk 258 | ); 259 | 260 | ---------------------------------------------------------------------------- 261 | -- Test access port: 262 | 263 | virtual_bus: 264 | VirtualBus 265 | generic map ( ADDR_W => VBUS_ADDR_W, NETPP_NAME => "TAP", 266 | BUSTYPE => BUS_GLOBAL ) 267 | port map ( 268 | clk => clk, 269 | wr => vbus_wr, 270 | rd => vbus_rd, 271 | wr_busy => '0', 272 | rd_busy => '0', 273 | addr => vbus_addr, 274 | data_in => vbus_din, 275 | data_out => vbus_dout 276 | ); 277 | 278 | vtap_ce <= vbus_wr or vbus_rd; 279 | 280 | registers: 281 | -- We use our own TAP decoder: 282 | entity ghdlex.decode_netppbus 283 | generic map ( DATA_WIDTH => 32 ) 284 | port map ( 285 | ce => vtap_ce, 286 | reset => global_startup_reset, 287 | ctrl => vtap_ctrl, 288 | stat => vtap_stat, 289 | data_in => vbus_din, 290 | data_out => vbus_dout, 291 | addr => vbus_addr(BV_MMR_CFG_netppbus), 292 | re => vbus_rd, 293 | we => vbus_wr, 294 | clk => clk 295 | ); 296 | 297 | -- tap: 298 | -- entity work.TestAccessPort 299 | -- port map ( 300 | -- tap_ctrl => vtap_ctrl, 301 | -- tap_stat => vtap_stat, 302 | -- clk => clk, 303 | -- reset => global_startup_reset 304 | -- ); 305 | -- 306 | -- Instancing the global bus that is accessible by netpp properties 307 | -- device_read() and device_write() 308 | global_throttle <= vtap_ctrl.throttle; 309 | 310 | 311 | stim: 312 | process 313 | variable retval : integer; 314 | begin 315 | if initialization < 0 then 316 | assert false report "netpp init failed" severity failure; 317 | end if; 318 | 319 | -- We use this signal to the client that we're ready 320 | vtap_stat.break <= '0'; 321 | 322 | global_startup_reset <= '1'; 323 | wait for 40 us; 324 | global_startup_reset <= '0'; 325 | 326 | -- Now stimulate: 327 | we <= '0'; 328 | data0 <= x"00112233"; 329 | addr <= "000000000000"; 330 | wait for 40 us; 331 | we <= '1'; 332 | wait for 20 us; 333 | we <= '0'; 334 | wait for 20 us; 335 | 336 | data0 <= x"deadbeef"; 337 | addr <= "000000000001"; 338 | wait for 40 us; 339 | we <= '1'; 340 | wait for 20 us; 341 | we <= '0'; 342 | wait for 20 us; 343 | 344 | data0 <= x"f00dface"; 345 | addr <= "000000000100"; 346 | wait for 40 us; 347 | we <= '1'; 348 | wait for 20 us; 349 | we <= '0'; 350 | wait for 20 us; 351 | 352 | vtap_stat.break <= '1'; 353 | 354 | wait; 355 | end process; 356 | 357 | ram_fill: 358 | process (dclk) 359 | begin 360 | if rising_edge(dclk) then 361 | addr_a <= addr_a + 1; 362 | data_a <= data_a + 1; 363 | end if; 364 | end process; 365 | 366 | dwe <= '1' when addr_a < 20 else '0'; 367 | 368 | ram_drain: 369 | process (clk) 370 | begin 371 | if rising_edge(clk) then 372 | addr_b <= addr_b + 1; 373 | end if; 374 | end process; 375 | 376 | end simulation; 377 | -------------------------------------------------------------------------------- /examples/fb.vhdl: -------------------------------------------------------------------------------- 1 | -- 2 | -- Simulator<->netpp server communication example 3 | -- (c) 2011 Martin Strubel 4 | -- 5 | -- This example connects to the netpp remote display and shows a UYUV color 6 | -- pattern. It is kinda slow, so deal with framerates < 1fps. 7 | -- 8 | -- Usage: Fire up display server on the local machine, then start this 9 | -- simulation. 10 | -- If it crashes, try using the --stack-size=10000 option 11 | 12 | -- There are two ways of doing it: Either dumping a memory buffer 13 | -- via setfb() or setting pixels by setpixel(). 14 | 15 | 16 | library ieee; 17 | use ieee.std_logic_1164.all; 18 | use ieee.numeric_std.all; -- Unsigned 19 | 20 | library ghdlex; 21 | use ghdlex.ghpi_netpp.all; 22 | use ghdlex.txt_util.all; 23 | 24 | use std.textio.all; 25 | 26 | entity simfb is 27 | generic ( 28 | ServerPort : string := "TCP:localhost:2008" 29 | ); 30 | end simfb; 31 | 32 | architecture behaviour of simfb is 33 | 34 | signal clk : std_ulogic := '0'; 35 | signal count : unsigned(15 downto 0) := x"0000"; 36 | signal data : unsigned(7 downto 0); 37 | signal sigterm : std_logic := '0'; 38 | 39 | signal ypos : unsigned(15 downto 0) := x"0000"; 40 | signal xpos : unsigned(15 downto 0) := x"0000"; 41 | 42 | constant WIDTH : integer := 256; 43 | constant HEIGHT : integer := 256; 44 | 45 | type rambuf is array(0 to WIDTH*HEIGHT-1) of pixel_t; 46 | 47 | shared variable device : netpphandle_t; 48 | shared variable g_fb : framebuffer_t; 49 | shared variable g_ram : rambuf; -- pixarray_t; 50 | 51 | begin 52 | 53 | process 54 | variable err : integer; 55 | variable hdl : handle_t; 56 | begin 57 | -- Open device. When failing to connect, function will bail out. 58 | device := device_open(ServerPort); 59 | -- get_ptr(device, hdl); 60 | -- get_ptr(device, hdl); 61 | print(output, "Got device handle: " & str(device)); 62 | g_fb := initfb(device, WIDTH, HEIGHT, VIDEOMODE_UYVY); 63 | if g_fb < 0 then 64 | assert false report "Failed to init framebuffer" severity failure; 65 | end if; 66 | clkloop : loop 67 | wait for 100 us; 68 | clk <= not clk; 69 | if sigterm = '1' then 70 | exit; 71 | end if; 72 | end loop clkloop; 73 | 74 | print(output, " -- TERMINATED --"); 75 | releasefb(g_fb); 76 | -- Close connection to netpp device 77 | device_close(device); 78 | wait; 79 | 80 | end process; 81 | 82 | ---------------------------------------------------------------------------- 83 | 84 | process (clk) 85 | variable ret : integer; 86 | variable offset : natural; 87 | variable val : unsigned(15 downto 0); 88 | variable uyselect : std_logic := '0'; 89 | begin 90 | if rising_edge(clk) then 91 | -- Set integer value on device to counter value: 92 | uyselect := not uyselect; 93 | if xpos = WIDTH - 1 then 94 | xpos <= (others => '0'); 95 | if ypos = HEIGHT - 1 then 96 | ypos <= (others => '0'); 97 | -- Update frame buffer 98 | setfb(g_fb, pixarray_t(g_ram)); 99 | updatefb(g_fb); 100 | count <= count + 1; 101 | else 102 | ypos <= ypos + 1; 103 | end if; 104 | else 105 | xpos <= xpos + 1; 106 | end if; 107 | 108 | if uyselect = '0' then 109 | val := count + xpos(7 downto 0); 110 | val := x"70" & val(7 downto 0); 111 | -- val := "0" & xpos(6 downto 0) & x"c0"; 112 | else 113 | val := count + ypos(7 downto 0); 114 | val := x"70" & val(7 downto 0); 115 | -- val := "0" & ypos(6 downto 0) & x"c0"; 116 | end if; 117 | offset := to_integer(ypos) * WIDTH + to_integer(xpos); 118 | g_ram(offset) := val; 119 | -- setpixel(g_fb, to_integer(xpos), to_integer(ypos), val); 120 | end if; 121 | end process; 122 | 123 | end; 124 | -------------------------------------------------------------------------------- /examples/netpp.vhdl: -------------------------------------------------------------------------------- 1 | -- 2 | -- Simulator <-> netpp server communication example 3 | -- (c) 2011 Martin Strubel 4 | -- 5 | -- Sets the control register on a netpp remote device to a value. 6 | -- 7 | -- Usage: start devices/example/slave from the netpp distribution, then 8 | -- run ./simnetpp to communicate with it. 9 | 10 | -- TODO: Turn this into a little virtual GUI driven FPGA board :-) 11 | 12 | library ieee; 13 | use ieee.std_logic_1164.all; 14 | use ieee.numeric_std.all; -- Unsigned 15 | 16 | library ghdlex; 17 | --! Use the ghpi_netpp package 18 | use ghdlex.ghpi_netpp.all; 19 | use ghdlex.txt_util.all; 20 | 21 | use std.textio.all; 22 | 23 | entity simnetpp is 24 | generic ( 25 | ServerPort : string := "TCP:localhost:2008" 26 | ); 27 | end simnetpp; 28 | 29 | architecture behaviour of simnetpp is 30 | 31 | signal clk : std_ulogic := '0'; 32 | signal count : unsigned(15 downto 0) := x"0000"; 33 | signal data : unsigned(7 downto 0); 34 | signal sigterm : std_logic := '0'; 35 | 36 | constant initialization : integer := netpp_init("", 0); 37 | 38 | shared variable device : netpphandle_t; 39 | shared variable t_int : token_t; 40 | 41 | begin 42 | 43 | process 44 | begin 45 | -- Open device. When failing to connect, function will bail out. 46 | device := device_open(ServerPort); 47 | print(output, "Got device handle: " & str(device)); 48 | -- Obtain TOKEN for 'ControlReg' property 49 | t_int := device_gettoken(device, "ControlReg"); 50 | if t_int = -1 then 51 | assert false report "Invalid token returned" severity failure; 52 | end if; 53 | clkloop : loop 54 | wait for 1 us; 55 | clk <= not clk; 56 | if sigterm = '1' then 57 | exit; 58 | end if; 59 | end loop clkloop; 60 | 61 | print(output, " -- TERMINATED --"); 62 | -- Close connection to netpp device 63 | device_close(device); 64 | wait; 65 | 66 | end process; 67 | 68 | ---------------------------------------------------------------------------- 69 | 70 | process (clk) 71 | variable ret : integer; 72 | begin 73 | if rising_edge(clk) then 74 | count <= count + 1; 75 | -- Set register value on device to counter value: 76 | ret := device_set_register(device, t_int, to_integer(count)); 77 | if ret < 0 then 78 | print(output, "Failed to set integer on peer"); 79 | end if; 80 | if count = x"0007" then 81 | sigterm <= '1'; 82 | end if; 83 | end if; 84 | end process; 85 | 86 | end; 87 | -------------------------------------------------------------------------------- /examples/pty.vhdl: -------------------------------------------------------------------------------- 1 | -- Simulator <-> C interfacing example via linux pipes 2 | -- (c) 2011 Martin Strubel 3 | -- 4 | -- 5 | -- Under Linux, run this command to create a virtual UART interface: 6 | -- 7 | -- > sudo socat PTY,link=/var/run/ghdlsim,raw,echo=0,user=`whoami` \ 8 | -- PTY,link=/var/run/iopipe,raw,echo=0,user=`whoami` 9 | -- 10 | -- Then open a terminal on the host side: 11 | -- 12 | -- > minicom -o -D /var/run/iopipe 13 | -- 14 | -- and run the simulation: 15 | -- ./simpty 16 | -- 17 | -- What you type into the terminal window is then echoed by the simulation. 18 | -- Also, you'll see the characters printed out in hex on the simulation 19 | -- windows. 20 | 21 | library ieee; 22 | use ieee.std_logic_1164.all; 23 | use ieee.numeric_std.all; -- Unsigned 24 | 25 | library ghdlex; 26 | use ghdlex.ghpi_pipe.all; 27 | use ghdlex.txt_util.all; 28 | 29 | use std.textio.all; 30 | 31 | entity simpty is end simpty; 32 | 33 | architecture behaviour of simpty is 34 | signal clk : std_ulogic := '0'; 35 | signal count : unsigned(7 downto 0) := x"05"; 36 | signal data : unsigned(7 downto 0); 37 | signal sigterm : std_logic := '0'; 38 | signal wr : std_logic := '0'; 39 | signal rx_ready : std_logic := '1'; 40 | signal data_valid : std_logic := '0'; 41 | signal pipe_flags : pipeflag_t := "0000"; 42 | -- Pipe handles: 43 | shared variable iopipe : pipehandle_t; 44 | 45 | begin 46 | process 47 | variable err : integer; 48 | begin 49 | iopipe := openpipe("/var/run/ghdlsim"); 50 | if iopipe < 0 then 51 | assert false report "Failed to open PTY pipe" severity failure; 52 | end if; 53 | clkloop : loop 54 | wait for 1 us; 55 | clk <= not clk; 56 | if sigterm = '1' then 57 | exit; 58 | end if; 59 | end loop clkloop; 60 | 61 | print(output, " -- TERMINATED --"); 62 | closepipe(iopipe); 63 | wait; 64 | 65 | end process; 66 | 67 | process (clk) 68 | variable val : unsigned(7 downto 0); 69 | variable flags : pipeflag_t; 70 | begin 71 | if rising_edge(clk) then 72 | 73 | flags := pipe_flags; 74 | 75 | -- Only call pipe if 76 | -- * Data ready to receive 77 | -- * Write command 78 | -- * There was something in the RX buffer 79 | 80 | if wr = '1' or rx_ready = '1' or flags(PIPE_RX) = '1' then 81 | val := data; 82 | flags(PIPE_TX) := wr; 83 | pipe_rxtx(iopipe, val, flags); 84 | end if; 85 | 86 | -- Did we get a byte? 87 | if pipe_flags(PIPE_RX) = '1' then 88 | data <= val; 89 | data_valid <= '1'; 90 | -- Terminate when we get Ctrl-E: 91 | if val = x"05" then 92 | sigterm <= '1'; 93 | end if; 94 | print(output, "SIM> " & hstr(val(8-1 downto 0))); 95 | wr <= '1'; 96 | else 97 | wr <= '0'; 98 | data_valid <= '0'; 99 | end if; 100 | 101 | -- Only file read on next cycle when requested: 102 | flags(PIPE_RX) := flags(PIPE_RX) and rx_ready; 103 | 104 | -- Save flags for next time 105 | pipe_flags <= flags; 106 | 107 | end if; 108 | end process; 109 | 110 | end; 111 | -------------------------------------------------------------------------------- /examples/ram.vhdl: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.std_logic_1164.all; 3 | use ieee.numeric_std.all; -- Unsigned 4 | 5 | library ghdlex; 6 | -- The RAM functions are generated within the netpp autowrapper 7 | use ghdlex.ghpi_netpp.all; 8 | use ghdlex.virtual.all; 9 | use ghdlex.txt_util.all; 10 | use std.textio.all; 11 | 12 | 13 | entity simram is 14 | end simram; 15 | 16 | architecture simulation of simram is 17 | constant initialization : integer := netpp_init("VirtualRAM", 2010); 18 | 19 | signal clk: std_logic := '0'; 20 | signal we: std_logic := '0'; 21 | constant ADDR_W : natural := 12; 22 | signal addr: unsigned(ADDR_W-1 downto 0) := (others => '0'); 23 | signal data0: unsigned(31 downto 0); 24 | signal data1: unsigned(15 downto 0); 25 | signal data2: unsigned(15 downto 0); 26 | 27 | begin 28 | 29 | clkgen: 30 | process 31 | begin 32 | clkloop : loop 33 | wait for 10 us; 34 | clk <= not clk; 35 | -- if sigterm = '1' then 36 | -- exit; 37 | -- end if; 38 | end loop clkloop; 39 | print(output, " -- TERMINATED --"); 40 | -- fifo_thread_exit; 41 | 42 | end process; 43 | 44 | ram0: 45 | DualPort16 46 | generic map ( 47 | -- NETPP_NAME => "LowWord", -- You could enable an explicit name 48 | ADDR_W => ADDR_W 49 | ) 50 | port map ( 51 | clk => clk, 52 | -- Port A 53 | a_we => we, 54 | a_addr => addr, 55 | a_write => data0(15 downto 0), 56 | a_read => data1, 57 | -- Port B 58 | b_we => '0', 59 | b_addr => addr, 60 | b_write => data0(15 downto 0), 61 | b_read => open 62 | ); 63 | 64 | ram1: 65 | DualPort16 generic map ( 66 | -- NETPP_NAME => "HighWord", -- You could enable an explicit name 67 | ADDR_W => ADDR_W 68 | ) 69 | port map ( 70 | clk => clk, 71 | -- Port A 72 | a_we => we, 73 | a_addr => addr, 74 | a_write => data0(31 downto 16), 75 | a_read => data2, 76 | -- Port B 77 | b_we => '0', 78 | b_addr => addr, 79 | b_write => data0(31 downto 16), 80 | b_read => open 81 | ); 82 | 83 | stim: 84 | process 85 | begin 86 | we <= '0'; 87 | data0 <= x"deadbeef"; 88 | addr <= "000000000001"; 89 | wait for 40 us; 90 | we <= '1'; 91 | wait for 20 us; 92 | we <= '0'; 93 | wait for 20 us; 94 | 95 | data0 <= x"f00dface"; 96 | addr <= "000000000010"; 97 | wait; 98 | end process; 99 | 100 | end simulation; 101 | -------------------------------------------------------------------------------- /fifo.py: -------------------------------------------------------------------------------- 1 | #!/bin/env/python 2 | 3 | # Simple test access of netpp simulation server 4 | 5 | NETPP = "/home/strubi/src/netpp" 6 | 7 | import sys 8 | sys.path.append(NETPP + "/Debug") 9 | sys.path.append(NETPP + "/python") 10 | 11 | import time 12 | import netpp 13 | 14 | def dump(seq): 15 | c = 0 16 | for i in seq: 17 | print "%02x" % (ord(i)), 18 | c += 1 19 | if c == 16: 20 | c = 0 21 | print 22 | print 23 | 24 | def find_mismatch(b0, b1): 25 | for i in range(len(b0)): 26 | if b0[i] != b1[i]: 27 | print "Mismatch from %d" % i, 28 | print "should be: %02x, is: %02x" % (ord(b0[i]), ord(b1[i])) 29 | break 30 | return i 31 | 32 | def test_blk(r, bufsize, throttle = 1, delay = 0.01): 33 | t = "" 34 | l = 0 35 | fifo = r.Fifo 36 | r.Throttle.set(throttle) 37 | 38 | COUNT_WRAP = 0x0100 39 | 40 | n = bufsize / 2 41 | 42 | should = "" 43 | c = 0 44 | for i in range(n): 45 | should += chr((c >> 8) & 0xff) + chr(c & 0xff) 46 | c += 1 47 | if c == COUNT_WRAP: 48 | c = 0 49 | 50 | 51 | a = netpp.Buffer(bufsize) 52 | 53 | print "buf size: %d bytes, reference size: %d" % (bufsize, len(should)) 54 | 55 | retry = 0 56 | while l < bufsize: 57 | fill = fifo.InFill.get() 58 | # print fill 59 | if fill >= bufsize: 60 | fifo.Buffer.get(a) 61 | # dump(a) 62 | t += str(a) 63 | l += len(a) 64 | retry = 0 65 | elif delay > 0.0: 66 | print "Polling... %.1f s (retry %d).." % (delay, retry) 67 | r.Throttle.set(0) 68 | time.sleep(delay) 69 | r.Throttle.set(throttle) 70 | retry += 1 71 | 72 | if retry > 10: 73 | print "Got nothing for 10 retries" 74 | break 75 | 76 | if len(t) > len(should): 77 | print "Truncated %d" % (len(t) - len(should)) 78 | t = t[:len(should)] 79 | 80 | if should != t: 81 | if len(t) != len(should): 82 | print "Length mismatch" 83 | else: 84 | i = find_mismatch(should, t) 85 | print "Position", i 86 | dump(should[i:i+16]) 87 | dump(t[i:i+16]) 88 | f = open("dump.bin", "w") 89 | f.write(t) 90 | f.close() 91 | else: 92 | print "Buffer ok!" 93 | 94 | 95 | dev = netpp.connect("localhost") 96 | 97 | r = dev.sync() 98 | enable = r.Enable 99 | reset = r.Reset 100 | 101 | enable.set(0) 102 | reset.set(1) 103 | time.sleep(0.1) 104 | reset.set(0) 105 | 106 | enable.set(1) 107 | 108 | # r.Throttle.set(0) 109 | # time.sleep(0.5) 110 | # r.Throttle.set(1) 111 | # time.sleep(0.1) 112 | # test_blk(r, 512, 0) 113 | # r.Throttle.set(0) 114 | # time.sleep(0.5) 115 | # r.Throttle.set(1) 116 | # time.sleep(0.1) 117 | 118 | # print "read block, throttled" 119 | # test_blk(r, 64, 1, 0.0) 120 | 121 | # d = buffer(20 * "0123456789") 122 | # r.Fifo.Buffer.set(d) 123 | 124 | print "read n blocks, accelerated" 125 | for i in range(8 * 64): 126 | test_blk(r, 512, 1, 1.0) 127 | 128 | print "read n blocks, non-throttled" 129 | for i in range(4): 130 | test_blk(r, 512, 0, 0.0) 131 | 132 | fifo = getattr(r, ":tb_soc:virtual_fifo:") 133 | 134 | print 80 * "-" 135 | print "Flush..." 136 | while fifo.InFill.get() > 128: 137 | test_blk(r, 128, 1, 0.1) 138 | 139 | # Let FIFO flood: 140 | 141 | r.Throttle.set(0) 142 | time.sleep(0.2) 143 | r.Throttle.set(1) 144 | 145 | print "See if more data coming..." 146 | try: 147 | c = fifo.Buffer.get() 148 | print "Had left in buffer:", len(c) 149 | except TypeError: 150 | print "Empty buffer" 151 | 152 | -------------------------------------------------------------------------------- /gensoc.mk: -------------------------------------------------------------------------------- 1 | SPACE = $(null) $(null) 2 | COMMA = , 3 | # Create comma separated list: 4 | MODULE_LIST=$(subst $(SPACE),$(COMMA),$(SOC_MODULES)) 5 | 6 | $(SOC_VHDL): 7 | $(GENSOC) -o ghdlex -sT \ 8 | --map-prefix=2 \ 9 | --decoder=$(MODULE_LIST) $(DEVICEFILE) 10 | 11 | GENERATED_VHDL-$(CONFIG_NETPP)= $(SOC_VHDL) 12 | 13 | -------------------------------------------------------------------------------- /ghdlex.mk: -------------------------------------------------------------------------------- 1 | TOPDIR ?= . 2 | 3 | RANLIB ?= ranlib 4 | 5 | CONFIG_NETPP = $(shell [ -e $(NETPP)/xml ] && echo y ) 6 | 7 | include $(GHDLEX)/src/project.mk 8 | GHDLEXSRCS = $(CSRCS:%.c=$(GHDLEX)/src/%.c) 9 | 10 | SIMOBJS = $(GHDLEXSRCS:%.c=%.o) 11 | 12 | PROPLIST ?= proplist.o 13 | 14 | $(LIBSIM).so: $(SIMOBJS) $(PROPLIST) 15 | $(CC) -o $@ -shared $(SIMOBJS) $(PROPLIST) -lpthread 16 | 17 | $(LIBSIM).a: $(SIMOBJS) $(PROPLIST) 18 | $(AR) ruv $@ $(SIMOBJS) $(PROPLIST) 19 | $(RANLIB) $@ 20 | 21 | 22 | ifdef CONFIG_MINGW32 23 | MYSIM_DUTIES = $(LIBSIM).a 24 | else 25 | MYSIM_DUTIES = $(LIBSIM).so 26 | endif 27 | 28 | mysim: $(MYSIM_DUTIES) 29 | 30 | clean:: 31 | $(MAKE) -C $(GHDLEX)/src clean 32 | 33 | .PHONY: mysim 34 | -------------------------------------------------------------------------------- /ghdlsim.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | section5 16 | 17 | 20 | 21 | 22 | 0 23 | 24 | 1 25 | 26 | develop 27 | 28 | 29 |
#include "example.h" 30 | #include "threadaux.h" 31 | #include "bus.h"
32 | 33 |
library ghdlex; use ghdlex.iomap_config.all;
34 | 35 | 38 | 39 | 40 | 41 | Could not start stream (File I/O error) 42 | 43 | 44 | 45 | Could not start capture 46 | 47 | 48 | 49 | Could not stop capture 50 | 51 | 52 | 53 | Video buffer queue error 54 | 55 | 56 | 57 | Video frame timeout 58 | 59 | 60 | 61 | Failed when polling for frame 62 | 63 | 64 | 65 | Could not get capture status 66 | 67 | 68 | 69 | Could not write stream (File I/O error) 70 | 71 | 72 | 73 | Could not open video device 74 | 75 | 76 | 77 | Could not allocate video buffers 78 | 79 | 80 | 81 | Failed to mmap() video buffer 82 | 83 | 84 | 85 | Could not set video format 86 | 87 | 88 | 89 | Bad machine state. Your code has a bug! 90 | 91 | 92 | 93 | Timeout on virtual Bus! 94 | 95 | 96 | 97 | Could not access i2c bus 98 | 99 | 100 | 101 | 102 | 103 | 0 104 | 105 | 2 106 | 107 | 108 | 109 | 110 | 111 | 112 | The bit fields in this register are dummies with respect to address decoding. 113 | They are used as bit slice defines within the VHDL top decoder section to 114 | pick the proper address signals from the top level address bus (or VirtualBus) 115 | For each I/O unit, a MMR_CFG_ entry is defined. 116 | Later, these slice defines will be moved to the iomap definitions for the entire SoC 117 | (see also memmap.xml for memory map) 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 127 | This register map definition with maximum register address VBUS_ADDR_OFFSET-1 refers to an internal simulated RAM that is repeatedly read by regmap_read(). This map is not accessible directly by defined netpp properties. 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | Firmware Revision MSB: major, LSB: Minor. Always at the end of the register map. 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | Default simulation registers, typically exported as netpp properties 148 | 149 | 150 | Control register 151 | 152 | 153 | Enable FPGA engine 154 | 155 | 156 | 157 | If set, slow down simulation 158 | 159 | 160 | 161 | 162 | 163 | Virtual IRQ pin, H active 164 | 165 | 166 | 167 | RESET FPGA engine 168 | 169 | 170 | 00X0XX10 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 2000 179 | 180 | 181 | 182 | 183 | 184 | Timeout value of FIFO in real microseconds 185 | 186 | g_fifoconfig.timeout 187 | 188 | 189 | 190 | Number of retries after timeout 191 | 192 | g_fifoconfig.retry 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | Virtual Input/Output. These properties can either control settings in the C library or map directly to a register. Depending on which register space they map to, the internal routines will either access the VirtualBus or the dummy register map (deprecated) 210 | 211 | 213 | 214 | 215 | Enable bit. Might be not used. 216 | 217 | 218 | 219 | 220 | 221 | External reset pin. High active. 222 | 223 | 224 | 225 | 226 | 227 | Simulates an IRQ pin. High active. Might not be implemented in all cores. 228 | 229 | 230 | 231 | 232 | 233 | 234 | This group only contains templates for internal generation of entities. Accessing them explicitely will have no effect or crash the system. 235 | 236 | 254 | 255 | 269 | 270 | 295 | 296 | 297 |
298 | -------------------------------------------------------------------------------- /h2vhdl.c: -------------------------------------------------------------------------------- 1 | /** API generator for VHDL 2 | * 3 | * (c) 2011, 2012, Martin Strubel 4 | * 5 | * This is a somewhat nasty hack to generate API functions for GHDL 6 | * in a semi automated way. 7 | * 8 | * The API definitions are found in apidef.h 9 | * 10 | */ 11 | 12 | #include "ghpi.h" 13 | #include 14 | #include 15 | 16 | // The C function prefix 17 | #define PREFIX "sim_" 18 | 19 | enum { 20 | TYPE_COMMENT, 21 | TYPE_FUNC, 22 | TYPE_PROC, 23 | }; 24 | 25 | struct param_desc { 26 | char *name; 27 | char *type; 28 | }; 29 | 30 | 31 | #define RUN_TYPES 32 | #include "apidef.h" 33 | #undef RUN_TYPES 34 | struct ghdl_apidesc { 35 | char *name; 36 | int type; 37 | const char *retitem; 38 | const char *parameters[16]; 39 | } g_api[] = { 40 | #include "apidef.h" 41 | { .name = NULL } 42 | }; 43 | 44 | int dump_header(FILE *f, char *item, struct ghdl_apidesc *d) 45 | { 46 | char const **p; 47 | int i = 0; 48 | 49 | fprintf(f, "%s %s", item, d->name); 50 | p = d->parameters; 51 | if (p[0]) { 52 | if (p[2]) { 53 | fprintf(f, "(%s : %s %s", p[0], p[2], p[1]); 54 | } else { 55 | fprintf(f, "(%s : %s", p[0], p[1]); 56 | } 57 | p += 3; i++; 58 | } 59 | while (p[0]) { 60 | if (p[2]) { 61 | fprintf(f, "; %s : %s %s", p[0], p[2], p[1]); 62 | } else { 63 | fprintf(f, "; %s : %s", p[0], p[1]); 64 | } 65 | p += 3; i++; 66 | } 67 | return i; 68 | } 69 | 70 | void dump_comment(FILE *f, struct ghdl_apidesc *d, int which) 71 | { 72 | if (which) { fprintf(f, "--! %s\n", d->name); } 73 | else { fprintf(f, "-- %s\n", d->name); } 74 | } 75 | 76 | void dump_func_decl(FILE *f, struct ghdl_apidesc *d) 77 | { 78 | dump_header(f, "function", d); 79 | fprintf(f, ") return %s", d->retitem); 80 | fprintf(f, ";\n"); 81 | fprintf(f, "\tattribute foreign of %s :\n" 82 | "\t\tfunction is \"VHPIDIRECT %s%s\";\n\n", d->name, PREFIX, d->name); 83 | } 84 | 85 | void dump_proc_decl(FILE *f, struct ghdl_apidesc *d) 86 | { 87 | int i; 88 | i = dump_header(f, "procedure", d); 89 | if (i) { 90 | fprintf(f, ");\n"); 91 | } else { 92 | fprintf(f, ";\n"); 93 | } 94 | fprintf(f, "\tattribute foreign of %s :\n" 95 | "\t\tprocedure is \"VHPIDIRECT %s%s\";\n\n", d->name, PREFIX, d->name); 96 | } 97 | void dump_func_body(FILE *f, struct ghdl_apidesc *d) 98 | { 99 | int i; 100 | i = dump_header(f, "function", d); 101 | if (i) { 102 | fprintf(f, ") return %s is\n", d->retitem); 103 | } else { 104 | fprintf(f, " return %s is\n", d->retitem); 105 | } 106 | fprintf(f, 107 | "begin\n" 108 | "\tassert false report \"VHPI\" severity failure;\n" 109 | "end function;\n\n"); 110 | } 111 | 112 | void dump_proc_body(FILE *f, struct ghdl_apidesc *d) 113 | { 114 | int i; 115 | i = dump_header(f, "procedure", d); 116 | if (i) { 117 | fprintf(f, ") is\n"); 118 | } else { 119 | fprintf(f, " is\n"); 120 | } 121 | fprintf(f, 122 | "begin\n" 123 | "\tassert false report \"VHPI\" severity failure;\n" 124 | "end procedure;\n\n"); 125 | } 126 | 127 | int dump_decl(FILE *f) 128 | { 129 | struct ghdl_apidesc *d = g_api; 130 | // struct param_desc *p; 131 | 132 | while(d->name) { 133 | switch (d->type) { 134 | case TYPE_FUNC: 135 | dump_func_decl(f, d); 136 | break; 137 | case TYPE_PROC: 138 | dump_proc_decl(f, d); 139 | break; 140 | default: 141 | dump_comment(f, d, 1); 142 | 143 | } 144 | d++; 145 | } 146 | return 0; 147 | } 148 | 149 | int dump_body(FILE *f) 150 | { 151 | struct ghdl_apidesc *d = g_api; 152 | d = g_api; 153 | while(d->name) { 154 | switch (d->type) { 155 | case TYPE_FUNC: 156 | dump_func_body(f, d); 157 | break; 158 | case TYPE_PROC: 159 | dump_proc_body(f, d); 160 | break; 161 | // Omit comments in body 162 | // default: 163 | // dump_comment(f, d, 0); 164 | } 165 | d++; 166 | } 167 | return 0; 168 | } 169 | 170 | const char g_line[] = 171 | "----------------------------------------------------------------------\n"; 172 | 173 | FILE *create_file(const char *fname, const char *rule) 174 | { 175 | FILE *f; 176 | char filename[256]; 177 | snprintf(filename, sizeof(filename) - 1, rule, fname); 178 | f = fopen(filename, "w"); 179 | if (!f) { 180 | fprintf(stderr, "Failed to open file for writing\n"); 181 | } 182 | return f; 183 | } 184 | 185 | int main(int argc, char **argv) 186 | { 187 | FILE *f; 188 | if (argc == 2) { 189 | f = create_file(argv[1], "%s_decl.chdl"); 190 | if (f) { 191 | dump_decl(f); 192 | fclose(f); 193 | } 194 | f = create_file(argv[1], "%s_body.chdl"); 195 | if (f) { 196 | dump_body(f); 197 | fclose(f); 198 | } 199 | } else { 200 | printf(g_line); 201 | return dump_decl(stdout); 202 | printf(g_line); 203 | return dump_body(stdout); 204 | } 205 | return 0; 206 | } 207 | -------------------------------------------------------------------------------- /hdl/iomap_config.vhdl: -------------------------------------------------------------------------------- 1 | library ieee; 2 | use ieee.numeric_std.all; 3 | 4 | package iomap_config is 5 | subtype regaddr_t is unsigned(7 downto 0); 6 | subtype REG_SIZE is integer range 15 downto 0; 7 | subtype BYTESLICE is integer range 7 downto 0; 8 | 9 | end iomap_config; 10 | -------------------------------------------------------------------------------- /hdl/libpipe.vhdl: -------------------------------------------------------------------------------- 1 | --! \file libpipe.vhdl Simple pipe I/O interface for linux named pipes 2 | -- (c) 2011, Martin Strubel 3 | -- 4 | 5 | library ieee; 6 | use ieee.std_logic_1164.all; 7 | use ieee.numeric_std.all; -- Unsigned 8 | 9 | --! \brief VHDL Unix Pipe interface 10 | --! This module implements a simple Unix pipe interface by using simple 11 | --! file I/O to externally created names pipes. 12 | --! 13 | --! To create a bidirectional pipe under Linux, it is recommended to 14 | --! use the socat utility as follows: 15 | --! \code 16 | --! > sudo socat PTY,link=/var/run/ghdlsim,raw,echo=0,user=`whoami` \ 17 | --! PTY,link=/var/run/iopipe,raw,echo=0,user=`whoami` 18 | --! \endcode 19 | --! 20 | --! Then open a terminal on the host side: 21 | --! 22 | --! \code 23 | --! > minicom -o -D /var/run/iopipe 24 | --! \endcode 25 | --! 26 | --! Now when you start the simulation, for instance simpty, you can speak 27 | --! to the simulation pipe through the minicom terminal. 28 | --! 29 | --! From the GHDL side, the pipe is read like a non-FIRST-FALL-THROUGH 30 | --! FIFO. That means, you will have to assert the RX flag prior to calling 31 | --! pipe_rxtx() to obtain a first valid byte. 32 | --! 33 | --! \defgroup GHPI_Pipe Unix Pipe interface 34 | --! \addtogroup GHPI_Pipe 35 | --! \{ 36 | package ghpi_pipe is 37 | subtype pipeflag_t is unsigned(0 to 3); --! Pipe flags array type 38 | constant PIPE_RX : natural := 0; --! in: Read advance, out: FIFO not empty 39 | constant PIPE_TX : natural := 1; --! in: Write advance, out: FIFO not full 40 | constant PIPE_OUR : natural := 2; --! out: Overrun/Underrun error 41 | constant PIPE_ERR : natural := 3; --! out: Generic error 42 | 43 | subtype pipehandle_t is integer; --! A pipe handle 44 | 45 | --! Opens a pipe 46 | --! \param name Filename of the pipe (must be created externally) 47 | --! \return Pipe handle, or error, if < 0 48 | function openpipe(name: string) 49 | return pipehandle_t; 50 | attribute foreign of openpipe : 51 | function is "VHPIDIRECT sim_openpipe"; 52 | 53 | --! Read/Write data and/or check read status 54 | --! \param data if flags(RX) is set, return a byte from the FIFO. 55 | --! The data is only valid when a previous flags(RX) was 56 | --! asserted. Otherwise, an underrun condition 57 | --! flags(OUR) = '1' will occur. 58 | --! If flags(TX) is set, write a byte to the FIFO. 59 | --! If the FIFO was not previously ready (flags(TX) = '0'), a 60 | --! FIFO overrun condition will occur likewise. 61 | 62 | --! \param flags in: read/write request (only RX and TX bit are checked) 63 | --! out: Data can be read when RX = '1' and 64 | --! Data can be written when TX = '1' 65 | 66 | procedure pipe_rxtx( 67 | handle : pipehandle_t; 68 | data : inout unsigned(7 downto 0); 69 | flags : inout pipeflag_t 70 | ); 71 | attribute foreign of pipe_rxtx : 72 | procedure is "VHPIDIRECT sim_pipe_rxtx"; 73 | 74 | --! Write data and/or check write status 75 | --! \param data if flags(TX) is set, write a byte to the FIFO. 76 | --! If the FIFO was not ready (flags(TX) = '0'), a 77 | --! FIFO overrun condition 78 | --! flags(OUR) = '1' will occur. 79 | --! \param flags in: FIFO write request (only TX bit is evaluated), 80 | --! out: Data can be written when TX = '1' 81 | 82 | --! Close pipe. This does not remove the actual named pipe file. 83 | procedure closepipe(handle: pipehandle_t); 84 | attribute foreign of closepipe : 85 | procedure is "VHPIDIRECT sim_closepipe"; 86 | 87 | end package; 88 | --! \} 89 | 90 | package body ghpi_pipe is 91 | function openpipe(name: string) 92 | return pipehandle_t is 93 | begin 94 | assert false report "VHPI" severity failure; 95 | end openpipe; 96 | 97 | procedure pipe_rxtx( 98 | handle : pipehandle_t; 99 | data : inout unsigned(7 downto 0); 100 | flags : inout pipeflag_t 101 | ) is 102 | begin 103 | assert false report "VHPI" severity failure; 104 | end pipe_rxtx; 105 | 106 | procedure closepipe(handle: pipehandle_t) is 107 | begin 108 | assert false report "VHPI" severity failure; 109 | end closepipe; 110 | 111 | end package body; 112 | -------------------------------------------------------------------------------- /hdl/libvirtual.vhdl: -------------------------------------------------------------------------------- 1 | --! \file libvirtual.vhdl Virtual entities package 2 | 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | use ieee.numeric_std.all; -- Unsigned 7 | 8 | --! \brief Virtual entity package 9 | --! Contains a number of virtual entities that are accessible through 10 | --! netpp. 11 | 12 | package virtual is 13 | 14 | type vram32_init_t is array(natural range <>) of 15 | unsigned(31 downto 0); 16 | 17 | type vram16_init_t is array(natural range <>) of 18 | unsigned(15 downto 0); 19 | 20 | --! Function to convert from a 16 bit data initialization 21 | function to_vram32_init(data : vram16_init_t) return vram32_init_t; 22 | 23 | component VirtualFIFO is 24 | generic ( 25 | NETPP_NAME : string := "DEFAULT"; 26 | FIFOSIZE : natural := 512; 27 | SLEEP_CYCLES : natural := 50000; 28 | WORDSIZE : natural := 1 29 | ); 30 | port ( 31 | signal clk : in std_logic; 32 | signal throttle : in std_logic; 33 | signal wr_ready : out std_logic; 34 | signal rd_ready : out std_logic; 35 | signal wr_enable : in std_logic; 36 | signal rd_enable : in std_logic; 37 | signal data_in : out std_logic_vector(8*WORDSIZE-1 downto 0); 38 | signal data_out : in std_logic_vector(8*WORDSIZE-1 downto 0) 39 | ); 40 | end component; 41 | 42 | -- A FIFO emulation for a Cypress FX2 43 | component VirtualFX2Fifo 44 | generic ( 45 | NETPP_NAME : string := "DEFAULT"; 46 | WORDSIZE : natural := 1 47 | ); 48 | port ( 49 | u_ifclk : in std_logic; -- USB interface clock 50 | u_slwr : in std_logic; 51 | u_slrd : in std_logic; 52 | u_sloe : in std_logic; 53 | u_pktend : out std_logic; 54 | u_flag : out std_logic_vector(2 downto 0); -- Status flags 55 | u_fifoadr : in std_logic_vector(1 downto 0); 56 | u_fd : inout std_logic_vector(15 downto 0) 57 | ); 58 | end component; 59 | 60 | 61 | component VirtualDualPortRAM is 62 | generic( 63 | NETPP_NAME : string := "DEFAULT"; 64 | DATA_W : natural := 32; 65 | ADDR_W : natural := 14; 66 | INIT_DATA : vram32_init_t := (0 => x"00000000") 67 | ); 68 | port( 69 | clk : in std_logic; 70 | -- Port A 71 | a_we : in std_logic; 72 | a_addr : in unsigned(ADDR_W-1 downto 0); 73 | a_write : in unsigned(DATA_W-1 downto 0); 74 | a_read : out unsigned(DATA_W-1 downto 0); 75 | -- Port B 76 | b_we : in std_logic; 77 | b_addr : in unsigned(ADDR_W-1 downto 0); 78 | b_write : in unsigned(DATA_W-1 downto 0); 79 | b_read : out unsigned(DATA_W-1 downto 0) 80 | ); 81 | end component VirtualDualPortRAM; 82 | 83 | component VirtualDualPortRAM_ce is 84 | generic( 85 | NETPP_NAME : string := "DEFAULT"; 86 | DATA_W : natural := 32; 87 | ADDR_W : natural := 14; 88 | INIT_DATA : vram32_init_t := (0 => x"00000000") 89 | ); 90 | port( 91 | clk : in std_logic; 92 | -- Port A 93 | a_ce : in std_logic; 94 | a_we : in std_logic; 95 | a_addr : in unsigned(ADDR_W-1 downto 0); 96 | a_write : in unsigned(DATA_W-1 downto 0); 97 | a_read : out unsigned(DATA_W-1 downto 0); 98 | -- Port B 99 | b_ce : in std_logic; 100 | b_we : in std_logic; 101 | b_addr : in unsigned(ADDR_W-1 downto 0); 102 | b_write : in unsigned(DATA_W-1 downto 0); 103 | b_read : out unsigned(DATA_W-1 downto 0) 104 | ); 105 | end component VirtualDualPortRAM_ce; 106 | 107 | 108 | component VirtualDualPortRAM_dc is 109 | generic( 110 | NETPP_NAME : string := "DEFAULT"; 111 | DATA_W : natural := 32; 112 | ADDR_W : natural := 14; 113 | EN_BYPASS : boolean := false; 114 | SYN_RAMTYPE : string := "simulation_only"; 115 | INIT_DATA : vram32_init_t := (0 => x"00000000") 116 | ); 117 | port( 118 | -- Port A 119 | a_clk : in std_logic; 120 | a_we : in std_logic; 121 | a_addr : in unsigned(ADDR_W-1 downto 0); 122 | a_write : in unsigned(DATA_W-1 downto 0); 123 | a_read : out unsigned(DATA_W-1 downto 0); 124 | -- Port B 125 | b_clk : in std_logic; 126 | b_we : in std_logic; 127 | b_addr : in unsigned(ADDR_W-1 downto 0); 128 | b_write : in unsigned(DATA_W-1 downto 0); 129 | b_read : out unsigned(DATA_W-1 downto 0) 130 | ); 131 | end component VirtualDualPortRAM_dc; 132 | 133 | 134 | component DualPort16 is 135 | generic( 136 | NETPP_NAME : string := "DEFAULT"; 137 | ADDR_W : natural := 14 138 | ); 139 | port( 140 | clk : in std_logic; 141 | -- Port A 142 | a_we : in std_logic; 143 | a_addr : in unsigned(ADDR_W-1 downto 0); 144 | a_write : in unsigned(16-1 downto 0); 145 | a_read : out unsigned(16-1 downto 0); 146 | -- Port B 147 | b_we : in std_logic; 148 | b_addr : in unsigned(ADDR_W-1 downto 0); 149 | b_write : in unsigned(16-1 downto 0); 150 | b_read : out unsigned(16-1 downto 0) 151 | ); 152 | end component DualPort16; 153 | 154 | component VirtualBus 155 | generic ( 156 | NETPP_NAME : string := "DEFAULT"; 157 | ADDR_W : natural := 8; 158 | DATA_W : natural := 32; 159 | BUSTYPE : natural := 1 160 | ); 161 | port ( 162 | clk : in std_logic; 163 | wr : out std_logic; 164 | rd : out std_logic; 165 | wr_busy : in std_logic; 166 | rd_busy : in std_logic; 167 | addr : out std_logic_vector(ADDR_W-1 downto 0); 168 | data_in : out std_logic_vector(DATA_W-1 downto 0); 169 | data_out : in std_logic_vector(DATA_W-1 downto 0) 170 | ); 171 | end component VirtualBus; 172 | 173 | end package; 174 | 175 | package body virtual is 176 | 177 | function to_vram32_init(data : vram16_init_t) return vram32_init_t is 178 | type vram_init_worker is array(0 to data'length-1) of unsigned(31 downto 0); 179 | variable work : vram_init_worker; 180 | begin 181 | for i in 0 to data'length-1 loop 182 | work(i) := resize(data(i), 32); 183 | end loop; 184 | return vram32_init_t(work); 185 | end function; 186 | 187 | 188 | end package body; 189 | 190 | -------------------------------------------------------------------------------- /hdl/txt_util.vhdl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hackfin/ghdlex/8f083926c8b9f6454f644dc587c97acedf9f0e3f/hdl/txt_util.vhdl -------------------------------------------------------------------------------- /hdl/vbus.vhdl: -------------------------------------------------------------------------------- 1 | --! \file vbus.vhdl Virtual Bus implementation 2 | 3 | -- (c) 2013 Martin Strubel 4 | 5 | library ieee; 6 | use ieee.std_logic_1164.all; 7 | use ieee.numeric_std.all; 8 | 9 | library work; 10 | use work.ghpi_netpp.all; -- For virtual register I/O (regmap_read()) 11 | use work.virtual.all; 12 | use work.txt_util.all; 13 | use std.textio.all; 14 | 15 | 16 | --! \brief Virtualized simple bus implementation 17 | --! 18 | --! The VirtualBus component implements a very simple read/write bus 19 | --! slave protocol for testing a (slave) interface from outside software. 20 | --! 21 | --! The I/O modes of this bus are either a READ or WRITE cycle. 22 | --! When writing, it is assumed that the data is accepted immediately 23 | --! when the 'wr' pin is asserted. 24 | --! In the READ case, there are delays to be dealt with. On a read request, 25 | --! the address asserted is valid and the data is expected to arrive one 26 | --! cycle later. Therefore, the remote caller has to wait for the data. 27 | --! This is achieved internally by a few FULL and READY flags. 28 | --! The netpp client typically addresses the virtual bus within its 29 | --! device_read() and device_write() functions. 30 | --! In the default case of this library, the device_ functions handle two 31 | --! address spaces: All addresses above #VBUS_ADDR_OFFSET access the 32 | --! VirtualBus, the ones below access the legacy emulated register map. 33 | --! 34 | --! Note that only accesses through the VirtualBus are guarded and handshaked, 35 | --! that means, legacy accesses are timing critical and the simulation 36 | --! does not feed back to the caller if a specific register update was 37 | --! noticed. 38 | 39 | entity VirtualBus is 40 | generic ( 41 | --! Name, as visible from netpp. If 'DEFAULT', the instanciation 42 | --! uses a generated name from the hierarchy. 43 | NETPP_NAME : string := "DEFAULT"; 44 | ADDR_W : natural := 8; --! Address bus width 45 | DATA_W : natural := 32; --! Data bus width 46 | BUSTYPE : natural := 1 --! 1 when global netpp bus 47 | ); 48 | port ( 49 | clk : in std_logic; --! The input master clock 50 | wr : out std_logic; --! Write request 51 | rd : out std_logic; --! Read request 52 | wr_busy : in std_logic; --! '1' when busy writing 53 | rd_busy : in std_logic; --! '1' when busy reading 54 | --! The address bus 55 | addr : out std_logic_vector(ADDR_W-1 downto 0); 56 | --! Data input 57 | data_in : out std_logic_vector(DATA_W-1 downto 0); 58 | --! Data output 59 | data_out : in std_logic_vector(DATA_W-1 downto 0) 60 | ); 61 | end entity VirtualBus; 62 | 63 | architecture simulation of VirtualBus is 64 | shared variable bus_handle : bus_t; 65 | signal dval : std_logic := '0'; 66 | signal iaddr : unsigned(ADDR_W-1 downto 0); 67 | 68 | signal ird : std_logic := '0'; 69 | signal iwr : std_logic := '0'; 70 | 71 | begin 72 | 73 | process 74 | variable ret : integer; 75 | begin 76 | if NETPP_NAME = "DEFAULT" then 77 | bus_handle := bus_new(simulation'path_name, DATA_W, BUSTYPE); 78 | else 79 | bus_handle := bus_new(NETPP_NAME, DATA_W, BUSTYPE); 80 | end if; 81 | if bus_handle = null then 82 | assert false report "Failed to register VirtualBus"; 83 | end if; 84 | wait_loop : loop 85 | wait for 10 us; 86 | end loop wait_loop; 87 | bus_del(bus_handle); 88 | wait; 89 | 90 | end process; 91 | 92 | bus_handler: 93 | process(clk) 94 | -- Flag assignment: 95 | -- 0: Read request 96 | -- 1: Write request 97 | -- 2: in: Data valid, out: Data ready 98 | variable flags : busflag_t := "000"; 99 | variable d_data : unsigned(DATA_W-1 downto 0); 100 | variable d_addr : unsigned(ADDR_W-1 downto 0); 101 | begin 102 | if rising_edge(clk) then 103 | d_data := unsigned(data_out); 104 | -- No RXTX when we just got a read request, because the result 105 | -- is one cycle delayed. 106 | -- if flags(2) = '1' then 107 | -- print(output, "Wback: " & hstr(d_data)); 108 | -- end if; 109 | 110 | if wr_busy = '0' then 111 | bus_rxtx(bus_handle, d_addr, d_data, flags); 112 | ird <= flags(0); 113 | iwr <= flags(1); 114 | else 115 | ird <= '0'; 116 | iwr <= '0'; 117 | end if; 118 | 119 | flags(2) := dval; 120 | iaddr <= d_addr; 121 | 122 | if flags(1) = '1' then -- Writing, latch data 123 | data_in <= std_logic_vector(d_data); 124 | end if; 125 | end if; 126 | end process; 127 | 128 | rd <= ird; wr <= iwr; 129 | addr <= std_logic_vector(iaddr); 130 | -- DVAL only used for reading: 131 | dval <= (not rd_busy) and (ird); 132 | end simulation; 133 | 134 | -------------------------------------------------------------------------------- /hdl/vfifo.vhdl: -------------------------------------------------------------------------------- 1 | --! \file vfifo.vhdl Virtualized FIFO implementation 2 | -- 3 | -- (c) 2011-2013 Martin Strubel 4 | -- 5 | -- Configureable WORDSIZE (1, 2) 6 | -- 7 | -- This is the VirtualFIFO variant, but allows multiple instances 8 | -- 9 | 10 | library ieee; 11 | use ieee.std_logic_1164.all; 12 | use ieee.numeric_std.all; 13 | 14 | library work; 15 | use work.ghpi_netpp.all; -- For virtual register I/O (regmap_read()) 16 | 17 | --! \brief A virtual FIFO component, accessible via netpp 18 | --! 19 | --! Note: This is a redesign (intermediate VFIFO entity) replacing the old 20 | --! legacy VirtualFIFO which could only be instanced once. This version 21 | --! allows multiple instances. 22 | --! 23 | --! It requires a netpp server to run in order to be accessed from the outside. 24 | --! Typically, you include this component into your simulation and run it with 25 | --! the --vpi=netpp.vpi argument or use the explicit netpp_init() call. 26 | --! 27 | --! This FIFO component works full duplex, unlike the FX2 emulation. 28 | --! A FIFO netpp property is simply a buffer that is read out or written 29 | --! to, just that the internal handling is different from a DualPort16 30 | --! virtual RAM. 31 | --! The default implementation of the FIFO property buffer reading is 32 | --! blocking, that means, the call to dcDevice_GetProperty() will wait 33 | --! until the requested data is ready. This can timeout under certain 34 | --! circumstances, for example, when the simulation is very complex and 35 | --! runs slow, such timeouts occur. In this case - and in general - it is 36 | --! recommended to read the 'Fifo.InFill' property that contains the 37 | --! number of available bytes in the FIFO. Likewise, 'Fifo.Outfill' can 38 | --! be probed to see if there are still bytes left in the FIFO out buffer 39 | --! to be read out by the simulation. 40 | --! 41 | 42 | entity VirtualFIFO is 43 | generic ( 44 | --! Name, as visible from netpp. If 'DEFAULT', the instanciation 45 | --! uses a generated name from the hierarchy. 46 | NETPP_NAME : string := "DEFAULT"; 47 | FIFOSIZE : natural := 512; --! FIFO size in number of words 48 | --! Sleep cycles on no activity. If 0, use sleep `global_waitcycles` 49 | SLEEP_CYCLES : natural := 50000; 50 | WORDSIZE : natural := 1 --! Word size in bytes [1,2] 51 | ); 52 | port ( 53 | signal clk : in std_logic; --! The input master clock 54 | signal throttle : in std_logic; --! Throttle input for simulation 55 | --! Signals by '1' when data is ready to be written 56 | signal wr_ready : out std_logic; 57 | --! Signals by '1' when data is ready to be read 58 | signal rd_ready : out std_logic; 59 | --! When '1', clock one data word into FIFO via data_out 60 | signal wr_enable : in std_logic; 61 | --! When '1', assert next data word from FIFO to data_in 62 | signal rd_enable : in std_logic; 63 | --! Data input (from FIFO to logic) 64 | signal data_in : out std_logic_vector(8*WORDSIZE-1 downto 0); 65 | --! Data output (from logic to FIFO) 66 | signal data_out : in std_logic_vector(8*WORDSIZE-1 downto 0) 67 | ); 68 | end entity; 69 | 70 | 71 | architecture simulation of VirtualFIFO is 72 | constant TX_PROG : natural := 0; 73 | constant TX_EMPTY : natural := 1; 74 | constant RX_FULL : natural := 2; 75 | 76 | constant DATA_WIDTH : natural := 8*WORDSIZE; 77 | 78 | signal fifo_flags : fifoflag_t := "000000"; 79 | 80 | shared variable fifo_handle : duplexfifo_t; 81 | 82 | begin 83 | 84 | process 85 | variable ret : integer; 86 | begin 87 | if NETPP_NAME = "DEFAULT" then 88 | fifo_handle := fifo_new(simulation'path_name, FIFOSIZE, WORDSIZE); 89 | else 90 | fifo_handle := fifo_new(NETPP_NAME, FIFOSIZE, WORDSIZE); 91 | end if; 92 | if fifo_handle = null then 93 | assert false report "Failed to register FIFO"; 94 | end if; 95 | wait_loop : loop 96 | wait for 10 us; 97 | end loop wait_loop; 98 | fifo_del(fifo_handle); 99 | wait; 100 | end process; 101 | 102 | -- External C fifo simulation: 103 | fifo_handler: 104 | process (clk) 105 | variable flags : fifoflag_t; 106 | variable d_data : unsigned(DATA_WIDTH-1 downto 0); 107 | begin 108 | if rising_edge(clk) then 109 | flags := rd_enable & wr_enable & "0000"; 110 | d_data := unsigned(data_out(DATA_WIDTH-1 downto 0)); 111 | fifo_rxtx(fifo_handle, d_data, flags); 112 | data_in <= std_logic_vector(d_data(DATA_WIDTH-1 downto 0)); 113 | 114 | if flags(FIFO_RXE) = '0' and throttle = '1' then 115 | if SLEEP_CYCLES /= 0 then 116 | usleep(SLEEP_CYCLES); 117 | else 118 | usleep(global_waitcycles); 119 | end if; 120 | end if; 121 | 122 | rd_ready <= flags(FIFO_RXE); 123 | wr_ready <= flags(FIFO_TXF); 124 | 125 | fifo_flags <= flags; 126 | end if; 127 | end process; 128 | 129 | end simulation; 130 | -------------------------------------------------------------------------------- /hdl/vfx2fifo.vhdl: -------------------------------------------------------------------------------- 1 | --! \file vfx2fifo.vhdl Virtual FX2 FIFO Wrapper for VirtualFIFO 2 | -- Software FIFO interface for GHDL simulator 3 | -- 4 | -- (c) 2011, Martin Strubel 5 | -- 6 | -- A virtual FIFO behaving like a FX2 7 | -- 8 | -- This is the typically the interface between netpp and a VHDL simulation. 9 | -- The word size is configureable (1, 2) using the WORDSIZE generic. 10 | 11 | library ieee; 12 | use ieee.std_logic_1164.all; 13 | use ieee.numeric_std.all; 14 | 15 | library work; 16 | use work.virtual.all; 17 | use work.ghpi_netpp.all; 18 | 19 | --! \brief Virtual FX2 FIFO 20 | --! Emulates a FX2 fifo with a software interface to speak to a netpp 21 | --! client. 22 | 23 | entity VirtualFX2Fifo is 24 | generic ( 25 | NETPP_NAME : string := "DEFAULT"; --! Default NETPP name 26 | WORDSIZE : natural := 1 --! Word size 27 | ); 28 | port ( 29 | u_ifclk : in std_logic; -- USB interface clock 30 | u_slwr : in std_logic; 31 | u_slrd : in std_logic; 32 | u_sloe : in std_logic; 33 | u_pktend : out std_logic; 34 | u_flag : out std_logic_vector(2 downto 0); -- Status flags 35 | u_fifoadr : in std_logic_vector(1 downto 0); 36 | u_fd : inout std_logic_vector(15 downto 0) 37 | ); 38 | end VirtualFX2Fifo; 39 | 40 | architecture behaviour of VirtualFX2Fifo is 41 | -- FIFO timings: 42 | -- FX2 compatible timings: 43 | constant T_SRD : time := 18.7 ns; 44 | constant T_XFLG : time := 9.5 ns; 45 | constant T_XFD : time := 11 ns; 46 | constant T_OEON : time := 10.5 ns; 47 | constant T_OEOFF : time := 10.5 ns; 48 | 49 | constant DATA_WIDTH : natural := 8*WORDSIZE; 50 | 51 | signal fifo_flags : fifoflag_t := "000000"; 52 | 53 | -- FIFO readahead 54 | signal can_rx : std_logic := '0'; 55 | signal can_tx : std_logic := '1'; 56 | 57 | signal slrd : std_logic := '1'; 58 | signal slwr : std_logic := '1'; 59 | signal sloe : std_logic := '1'; 60 | 61 | signal re : std_logic; 62 | signal we : std_logic; 63 | 64 | signal data : std_logic_vector(DATA_WIDTH-1 downto 0) 65 | := (others => '0'); 66 | 67 | begin 68 | 69 | -- /OE control 70 | oectrl: 71 | process (u_sloe, data) 72 | begin 73 | if u_sloe = '0' then 74 | u_fd <= (others => 'Z'); 75 | u_fd <= std_logic_vector( 76 | resize(unsigned(data), u_fd'length)) after T_OEON; 77 | else 78 | u_fd <= (others => 'Z') after T_OEOFF; 79 | end if; 80 | end process; 81 | 82 | virtual_fifo: VirtualFIFO 83 | generic map ( 84 | NETPP_NAME => NETPP_NAME, 85 | WORDSIZE => WORDSIZE 86 | ) 87 | port map ( 88 | clk => u_ifclk, 89 | throttle => global_throttle, 90 | wr_ready => can_tx, 91 | rd_ready => can_rx, 92 | wr_enable => we, 93 | rd_enable => re, 94 | data_in => data, 95 | data_out => u_fd(8*WORDSIZE-1 downto 0) 96 | ); 97 | 98 | we <= not slwr; 99 | re <= not slrd; 100 | 101 | u_flag(TX_EMPTY) <= can_rx after T_XFLG; 102 | u_flag(RX_FULL) <= can_tx after T_XFLG; 103 | 104 | -- Simulate setup timing for internal signals: 105 | sloe <= u_sloe after T_OEON; 106 | slrd <= u_slrd after T_SRD; 107 | slwr <= u_slwr after T_SRD; 108 | 109 | end behaviour; 110 | -------------------------------------------------------------------------------- /hdl/vram.vhdl: -------------------------------------------------------------------------------- 1 | --! \file vram.vhdl Virtual n-bit RAM (up to 32 bit) for backdoor access 2 | --! 3 | 4 | -- 5 | -- (c) 2011-2018 Martin Strubel 6 | -- 7 | -- Configureable WORDSIZE (1, 2) 8 | -- 9 | -- 10 | 11 | library ieee; 12 | use ieee.std_logic_1164.all; 13 | use ieee.numeric_std.all; -- Unsigned 14 | 15 | library work; 16 | -- The RAM functions are generated within the netpp autowrapper 17 | use work.ghpi_netpp.all; 18 | use work.virtual.all; 19 | 20 | --! \brief Dual port RAM with simulation interface via netpp 21 | --! 22 | --! This RAM registers itself as a netpp property and can be addressed 23 | --! under its instance name from outside, provided the netpp.vpi module 24 | --! is loaded or initialized from within the simulation. 25 | --! 26 | --! \version 0.2 'reset' port eliminated. If a resetable RAM 27 | --! (simulation side) is ever required again, implement 28 | --! it under another name! 29 | entity VirtualDualPortRAM_ce is 30 | generic( 31 | NETPP_NAME : string := "DEFAULT"; --! netpp entity name 32 | DATA_W : natural := 32; --! Data width (bits) 33 | ADDR_W : natural := 14; --! Address bits 34 | --! Initialization data 35 | INIT_DATA : vram32_init_t := (0 => x"00000000") 36 | ); 37 | port( 38 | clk : in std_logic; --! Clock for Port A and B 39 | -- Port A 40 | a_ce : in std_logic; --! Enable signal 41 | a_we : in std_logic; --! A write enable (high active) 42 | a_addr : in unsigned(ADDR_W-1 downto 0); --! Port A Address 43 | a_write : in unsigned(DATA_W-1 downto 0); --! Port A write data 44 | a_read : out unsigned(DATA_W-1 downto 0); --! Read data 45 | -- Port B 46 | b_ce : in std_logic; --! Enable signal 47 | b_we : in std_logic; --! B write enable 48 | b_addr : in unsigned(ADDR_W-1 downto 0); --! B address 49 | b_write : in unsigned(DATA_W-1 downto 0); --! B write data 50 | b_read : out unsigned(DATA_W-1 downto 0) --! B read data 51 | ); 52 | end VirtualDualPortRAM_ce; 53 | 54 | architecture simulation of VirtualDualPortRAM_ce is 55 | shared variable ram_handle : rambuf_t; 56 | 57 | procedure ram_init (data : in vram32_init_t) is 58 | variable size : integer; 59 | variable wdat : ram_port_t; 60 | variable addr : unsigned(ADDR_W-1 downto 0); 61 | begin 62 | size := (2**ADDR_W); 63 | 64 | if data'length < size then 65 | size := data'length; 66 | assert false 67 | report "Init data not specified or less than RAM size" 68 | severity warning; 69 | 70 | elsif data'length > size then 71 | assert false 72 | report "Init data size mismatch, not initializing" 73 | severity failure; 74 | end if; 75 | 76 | for i in 0 to size-1 loop 77 | addr := to_unsigned(i, ADDR_W); 78 | wdat := resize(data(i)(DATA_W-1 downto 0), wdat'length); 79 | ram_write(ram_handle, addr, wdat); 80 | end loop; 81 | end procedure; 82 | 83 | begin 84 | 85 | -- Initialization within simulation: 86 | process 87 | begin 88 | if NETPP_NAME = "DEFAULT" then 89 | ram_handle := ram_new(simulation'path_name, DATA_W, ADDR_W); 90 | else 91 | ram_handle := ram_new(NETPP_NAME, DATA_W, ADDR_W); 92 | end if; 93 | if ram_handle = null then 94 | assert false report "Failed to reserve RAM buffer"; 95 | end if; 96 | ram_init(INIT_DATA); 97 | wait; 98 | ram_del(ram_handle); -- We never get here.. 99 | end process; 100 | 101 | process(clk) 102 | variable err: integer; 103 | variable addr_a: unsigned(ADDR_W-1 downto 0); 104 | variable addr_b: unsigned(ADDR_W-1 downto 0); 105 | variable wdata_a: ram_port_t; 106 | variable wdata_b: ram_port_t; 107 | variable rdata_a: ram_port_t; 108 | variable rdata_b: ram_port_t; 109 | begin 110 | addr_a := a_addr; 111 | addr_b := b_addr; 112 | wdata_a := resize(a_write, wdata_a'length); 113 | wdata_b := resize(b_write, wdata_b'length); 114 | if rising_edge(clk) then 115 | if a_ce = '1' then 116 | if a_we = '1' then 117 | if b_we = '1' then 118 | assert false report "Write collision"; 119 | end if; 120 | ram_write(ram_handle, addr_a, wdata_a); 121 | rdata_a := wdata_a; -- bypass 122 | elsif b_ce = '1' and b_we = '1' and addr_a = addr_b then 123 | rdata_a := wdata_b; 124 | else 125 | ram_read(ram_handle, addr_a, rdata_a); 126 | end if; 127 | end if; 128 | 129 | if b_ce = '1' then 130 | if b_we = '1' then 131 | ram_write(ram_handle, addr_b, wdata_b); 132 | rdata_b := wdata_b; -- bypass 133 | elsif a_ce = '1' and a_we = '1' and addr_a = addr_b then 134 | rdata_b := wdata_a; 135 | else 136 | ram_read(ram_handle, addr_b, rdata_b); 137 | end if; 138 | end if; 139 | 140 | end if; 141 | a_read <= rdata_a(DATA_W-1 downto 0); 142 | b_read <= rdata_b(DATA_W-1 downto 0); 143 | end process; 144 | 145 | end simulation; 146 | 147 | library ieee; 148 | use ieee.std_logic_1164.all; 149 | use ieee.numeric_std.all; -- Unsigned 150 | 151 | library work; 152 | -- The RAM functions are generated within the netpp autowrapper 153 | use work.ghpi_netpp.all; 154 | use work.virtual.all; 155 | 156 | entity VirtualDualPortRAM is 157 | generic( 158 | NETPP_NAME : string := "DEFAULT"; --! netpp entity name 159 | DATA_W : natural := 32; --! Data width (bits) 160 | ADDR_W : natural := 14; --! Address bits 161 | --! Initialization data 162 | INIT_DATA : vram32_init_t := (0 => x"00000000") 163 | ); 164 | port( 165 | clk : in std_logic; --! Clock for Port A and B 166 | -- Port A 167 | a_we : in std_logic; --! A write enable (high active) 168 | a_addr : in unsigned(ADDR_W-1 downto 0); --! Port A Address 169 | a_write : in unsigned(DATA_W-1 downto 0); --! Port A write data 170 | a_read : out unsigned(DATA_W-1 downto 0); --! Read data 171 | -- Port B 172 | b_we : in std_logic; --! B write enable 173 | b_addr : in unsigned(ADDR_W-1 downto 0); --! B address 174 | b_write : in unsigned(DATA_W-1 downto 0); --! B write data 175 | b_read : out unsigned(DATA_W-1 downto 0) --! B read data 176 | ); 177 | end VirtualDualPortRAM; 178 | 179 | 180 | architecture simulation of VirtualDualPortRAM is 181 | 182 | begin 183 | 184 | wrapper: 185 | entity work.VirtualDualPortRAM_ce 186 | generic map ( 187 | NETPP_NAME => NETPP_NAME , 188 | DATA_W => DATA_W, 189 | ADDR_W => ADDR_W, 190 | INIT_DATA => INIT_DATA 191 | ) 192 | port map ( 193 | clk => clk, 194 | a_ce => '1', 195 | a_we => a_we, 196 | a_addr => a_addr, 197 | a_write => a_write, 198 | a_read => a_read, 199 | b_ce => '1', 200 | b_we => b_we, 201 | b_addr => b_addr, 202 | b_write => b_write, 203 | b_read => b_read 204 | ); 205 | 206 | end simulation; 207 | -------------------------------------------------------------------------------- /hdl/vram16.vhdl: -------------------------------------------------------------------------------- 1 | --! \file vram16.vhdl Virtual 16 bit RAM for backdoor access 2 | library ieee; 3 | use ieee.std_logic_1164.all; 4 | use ieee.numeric_std.all; -- Unsigned 5 | 6 | library work; 7 | -- The RAM functions are generated within the netpp autowrapper 8 | use work.ghpi_netpp.all; 9 | 10 | --! \brief Legacy RAM with simulation interface via netpp 11 | --! 12 | --! \deprecated This is an old version, not implementing bypass logic 13 | --! for simultaneous R/W. Also, API has changed, and endiannes 14 | --! fixes have been applied. Use the VirtualDualPortRAM entity. 15 | --! 16 | --! 17 | entity DualPort16 is 18 | generic( 19 | NETPP_NAME : string := "DEFAULT"; 20 | ADDR_W : natural := 14 21 | ); 22 | port( 23 | clk : in std_logic; --! Clock for Port A and B 24 | -- Port A 25 | a_we : in std_logic; --! A write enable (high active) 26 | a_addr : in unsigned(ADDR_W-1 downto 0); --! Port A Address 27 | a_write : in unsigned(16-1 downto 0); --! Port A write data 28 | a_read : out unsigned(16-1 downto 0); --! Read data 29 | -- Port B 30 | b_we : in std_logic; --! B write enable 31 | b_addr : in unsigned(ADDR_W-1 downto 0); --! B address 32 | b_write : in unsigned(16-1 downto 0); --! B write data 33 | b_read : out unsigned(16-1 downto 0) --! B read data 34 | ); 35 | end DualPort16; 36 | 37 | architecture simulation of DualPort16 is 38 | shared variable ram_handle : rambuf_t; 39 | begin 40 | 41 | -- Initialization within simulation: 42 | process 43 | begin 44 | if NETPP_NAME = "DEFAULT" then 45 | ram_handle := ram_new(simulation'path_name, 16, ADDR_W); 46 | else 47 | ram_handle := ram_new(NETPP_NAME, 16, ADDR_W); 48 | end if; 49 | if ram_handle = null then 50 | assert false report "Failed to reserve RAM buffer"; 51 | end if; 52 | wait; 53 | ram_del(ram_handle); -- We never get here.. 54 | end process; 55 | 56 | process(clk) 57 | variable err: integer; 58 | variable addr_a: unsigned(ADDR_W-1 downto 0); 59 | variable addr_b: unsigned(ADDR_W-1 downto 0); 60 | variable wdata_a: ram_port_t; 61 | variable wdata_b: ram_port_t; 62 | variable rdata_a: ram_port_t; 63 | variable rdata_b: ram_port_t; 64 | begin 65 | addr_a := a_addr; 66 | addr_b := b_addr; 67 | wdata_a := resize(a_write, wdata_a'length); 68 | wdata_b := resize(b_write, wdata_b'length); 69 | if rising_edge(clk) then 70 | if a_we = '1' then 71 | ram_write(ram_handle, addr_a, wdata_a); 72 | elsif b_we = '1' then 73 | ram_write(ram_handle, addr_b, wdata_b); 74 | else 75 | ram_read(ram_handle, addr_a, rdata_a); 76 | ram_read(ram_handle, addr_b, rdata_b); 77 | end if; 78 | end if; 79 | a_read <= rdata_a(a_read'range); 80 | b_read <= rdata_b(b_read'range); 81 | end process; 82 | 83 | end simulation; 84 | -------------------------------------------------------------------------------- /hdl/vram_dclk.vhdl: -------------------------------------------------------------------------------- 1 | --! \file vram_dclk.vhdl Virtual n-bit RAM (up to 32 bit) (dual clock) 2 | 3 | library ieee; 4 | use ieee.std_logic_1164.all; 5 | use ieee.numeric_std.all; -- Unsigned 6 | 7 | library work; 8 | -- The RAM functions are generated within the netpp autowrapper 9 | use work.ghpi_netpp.all; 10 | use work.virtual.all; 11 | 12 | --! \brief Dual port/dual clock RAM with simulation interface via netpp 13 | --! \since 0.2 14 | --! Experimental dual clock version of the VirtualDualPortRAM component 15 | --! Does not check for collisions 16 | --! 17 | --! \endcode 18 | --! 19 | entity VirtualDualPortRAM_dc is 20 | generic( 21 | NETPP_NAME : string := "DEFAULT"; --! netpp entity name 22 | DATA_W : natural := 32; --! Data width (bits) 23 | ADDR_W : natural := 14; --! Address bits 24 | EN_BYPASS : boolean := false; --! Unused, compatibility 25 | SYN_RAMTYPE : string := "simulation_only"; --! Unused (tag) 26 | --! Initialization data 27 | INIT_DATA : vram32_init_t := (0 => x"00000000") 28 | ); 29 | port( 30 | -- Port A 31 | a_clk : in std_logic; --! Clock for Port A 32 | a_we : in std_logic; --! A write enable (high active) 33 | a_addr : in unsigned(ADDR_W-1 downto 0); --! Port A Address 34 | a_write : in unsigned(DATA_W-1 downto 0); --! Port A write data 35 | a_read : out unsigned(DATA_W-1 downto 0); --! Read data 36 | -- Port B 37 | b_clk : in std_logic; --! Clock for Port B 38 | b_we : in std_logic; --! B write enable 39 | b_addr : in unsigned(ADDR_W-1 downto 0); --! B address 40 | b_write : in unsigned(DATA_W-1 downto 0); --! B write data 41 | b_read : out unsigned(DATA_W-1 downto 0) --! B read data 42 | ); 43 | end VirtualDualPortRAM_dc; 44 | 45 | architecture simulation of VirtualDualPortRAM_dc is 46 | shared variable ram_handle : rambuf_t; 47 | 48 | procedure ram_init (data : in vram32_init_t) is 49 | variable size : integer; 50 | variable wdat : ram_port_t; 51 | variable addr : unsigned(ADDR_W-1 downto 0); 52 | begin 53 | size := (2**ADDR_W); 54 | 55 | if data'length < size then 56 | size := data'length; 57 | assert false 58 | report "Init data not specified or less than RAM size" 59 | severity warning; 60 | 61 | elsif data'length > size then 62 | assert false 63 | report "Init data size mismatch, not initializing" 64 | severity failure; 65 | end if; 66 | 67 | for i in 0 to size-1 loop 68 | addr := to_unsigned(i, ADDR_W); 69 | wdat := resize(data(i)(DATA_W-1 downto 0), wdat'length); 70 | ram_write(ram_handle, addr, wdat); 71 | end loop; 72 | end procedure; 73 | 74 | begin 75 | 76 | -- Initialization within simulation: 77 | process 78 | begin 79 | if NETPP_NAME = "DEFAULT" then 80 | ram_handle := ram_new(simulation'path_name, DATA_W, ADDR_W); 81 | else 82 | ram_handle := ram_new(NETPP_NAME, DATA_W, ADDR_W); 83 | end if; 84 | if ram_handle = null then 85 | assert false report "Failed to reserve RAM buffer"; 86 | end if; 87 | ram_init(INIT_DATA); 88 | wait; 89 | ram_del(ram_handle); -- We never get here.. 90 | end process; 91 | 92 | porta_proc: 93 | process(a_clk) 94 | variable addr_a: unsigned(ADDR_W-1 downto 0); 95 | variable wdata_a: ram_port_t; 96 | variable rdata_a: ram_port_t; 97 | begin 98 | addr_a := a_addr; 99 | wdata_a := resize(a_write, wdata_a'length); 100 | if rising_edge(a_clk) then 101 | if a_we = '1' then 102 | ram_write(ram_handle, addr_a, wdata_a); 103 | rdata_a := wdata_a; -- bypass 104 | else 105 | ram_read(ram_handle, addr_a, rdata_a); 106 | end if; 107 | 108 | end if; 109 | a_read <= rdata_a(DATA_W-1 downto 0); 110 | end process; 111 | 112 | portb_proc: 113 | process(b_clk) 114 | variable addr_b: unsigned(ADDR_W-1 downto 0); 115 | variable wdata_b: ram_port_t; 116 | variable rdata_b: ram_port_t; 117 | begin 118 | addr_b := b_addr; 119 | wdata_b := resize(b_write, wdata_b'length); 120 | if rising_edge(b_clk) then 121 | if b_we = '1' then 122 | ram_write(ram_handle, addr_b, wdata_b); 123 | rdata_b := wdata_b; -- bypass 124 | else 125 | ram_read(ram_handle, addr_b, rdata_b); 126 | end if; 127 | 128 | end if; 129 | b_read <= rdata_b(DATA_W-1 downto 0); 130 | end process; 131 | 132 | end simulation; 133 | -------------------------------------------------------------------------------- /lib.mk: -------------------------------------------------------------------------------- 1 | # Makefile auxiliary to create GHDLEX library 2 | 3 | GHDLEX ?= $(CURDIR) 4 | GHDL ?= ghdl 5 | 6 | VHDL_STD_SUFFIX ?= 93 7 | 8 | ifndef CONFIG_NETPP 9 | CONFIG_NETPP = $(shell [ -e $(NETPP)/xml ] && echo y ) 10 | endif 11 | 12 | PREFIX ?= . 13 | 14 | GHDLEX_VHDL_DIR = $(GHDLEX)/hdl 15 | 16 | GHDLEX_VHDL = \ 17 | $(GHDLEX_VHDL_DIR)/libpipe.vhdl \ 18 | $(GHDLEX_VHDL_DIR)/libvirtual.vhdl \ 19 | $(GHDLEX_VHDL_DIR)/vbus.vhdl \ 20 | $(GHDLEX_VHDL_DIR)/vram.vhdl \ 21 | $(GHDLEX_VHDL_DIR)/vram_dclk.vhdl \ 22 | $(GHDLEX_VHDL_DIR)/vram16.vhdl \ 23 | $(GHDLEX_VHDL_DIR)/vfifo.vhdl \ 24 | $(GHDLEX_VHDL_DIR)/vfx2fifo.vhdl \ 25 | $(GHDLEX_VHDL_DIR)/iomap_config.vhdl \ 26 | $(GHDLEX_VHDL_DIR)/txt_util.vhdl 27 | 28 | # Default compatibility layer for opensource release 29 | ifeq ($(CONFIG_NETPP),y) 30 | GHDLEX_VHDL += $(GHDLEX)/libnetpp.vhdl $(GHDLEX)/ghdlex_iomap_pkg.vhdl 31 | GHDLEX_VHDL += $(GHDLEX)/ghdlex_netppbus_decode.vhdl 32 | endif 33 | 34 | $(PREFIX)/ghdlex-obj$(VHDL_STD_SUFFIX).cf: $(GHDLEX_VHDL) 35 | [ -e $(PREFIX) ] || mkdir $(PREFIX) 36 | $(GHDL) -i --std=$(VHDL_STD) \ 37 | --workdir=$(PREFIX) --work=ghdlex $(GHDLEX_VHDL) 38 | 39 | all: $(PREFIX)/ghdlex-obj$(VHDL_STD_SUFFIX).cf 40 | 41 | DUTIES += $(PREFIX)/ghdlex-obj$(VHDL_STD_SUFFIX).cf 42 | -------------------------------------------------------------------------------- /libfifo.vhdl: -------------------------------------------------------------------------------- 1 | --! \file libfifo.vhdl I/O interface for thread controlled FIFO 2 | -- (c) 2011, Martin Strubel 3 | -- 4 | 5 | 6 | library ieee; 7 | use ieee.std_logic_1164.all; 8 | use ieee.numeric_std.all; -- Unsigned 9 | 10 | --! \brief VHDL FIFO interface 11 | --! 12 | --! \deprecated DO NOT USE THIS API ANYMORE. 13 | --! Use the FIFO API from libnetpp.chdl. 14 | --! 15 | --! Simple virtual FIFO module 16 | --! Implements a first-fall-through FIFO accessible through network 17 | --! via the netpp library. 18 | --! Either the VirtualFIFO component can be integrated into the design, 19 | --! or the FIFO functions can be called on a lower level. 20 | --! The data in/out word size is configureable using the WORD_SIZE generic. 21 | --! (Supported values: 1, 2) 22 | --! 23 | --! \defgroup GHPI_Fifo Old deprecated VHDL FIFO interface 24 | --! \addtogroup GHPI_Fifo 25 | --! \{ 26 | package ghpi_fifo is 27 | 28 | --! Set this variable to true to terminate FIFO thread 29 | shared variable fifo_terminate : boolean := false; 30 | 31 | subtype fdata is unsigned; 32 | subtype fifoflag_t is unsigned(0 to 5); 33 | -- FIFO flag indices: 34 | constant RX : natural := 0; --! in: Read advance, out: FIFO not empty 35 | constant TX : natural := 1; --! in: Write advance, out: FIFO not full 36 | constant RXE : natural := 2; --! out: LOW when FIFO almost not empty 37 | constant TXF : natural := 3; --! out: LOW when FIFO almost full 38 | constant OVR : natural := 4; --! out: overrun bit. Write 1 to clear. 39 | constant UNR : natural := 5; --! out: underrun bit. W1C. 40 | 41 | --! Init the external FIFO thread 42 | --! \param arg Currently an empty string, unused. 43 | --! \param wsize 1: Word size 8 bits, 2: 16 bits 44 | function fifo_thread_init(arg: string; wsize: integer) return integer; 45 | 46 | -- This is just a wrapper for the above function 47 | function sim_fifo_thread_init(arg: string; wsize: integer) return integer; 48 | attribute foreign of sim_fifo_thread_init : 49 | function is "VHPIDIRECT sim_fifo_thread_init"; 50 | 51 | --! Shutdown external FIFO thread 52 | procedure fifo_thread_exit; 53 | attribute foreign of fifo_thread_exit : procedure is "VHPIDIRECT fifo_thread_exit"; 54 | 55 | --! The FIFO I/O routine. 56 | --! \param data Pointer to data being written, if TX flag set, 57 | --! Data is modified with the 'read' FIFO data when RX 58 | --! flag set. If FIFO is not ready for READ or WRITE 59 | --! operation (empty or full), an underrun respective 60 | --! overrun condition will occur and be signalled in the 61 | --! flags(OVR) and flags(UNR) bits. Clearing this error 62 | --! condition is achieved by setting these error flags to 63 | --! '1'. 64 | --! The word size of the data bit vector is defined by 65 | --! the wsize argument to fifo_thread_init() 66 | --! \param flags The FIFO control (in) and status (out) flags. 67 | --! When calling this function the first time, you should 68 | --! check the status by setting all flags to '0'. 69 | --! On return, the (RX) and (TX) fill state can be read 70 | --! from the RXE and TXF flags (for burst reading) and 71 | --! from the RX and TX flags (current, absolute FIFO fill 72 | --! state. On subsequent calls, the RX and TX flags 73 | --! indicate, which action (Read word/Write word) should 74 | --! be taken. The current status is always returned in the 75 | --! flags array. 76 | procedure fifo_io( 77 | data: inout fdata; 78 | flags : inout fifoflag_t 79 | ); 80 | attribute foreign of fifo_io : procedure is 81 | "VHPIDIRECT sim_fifo_io"; 82 | 83 | -- FLAG assignments for FX2 emulation 84 | constant TX_PROG : natural := 0; 85 | constant TX_EMPTY : natural := 1; 86 | constant RX_FULL : natural := 2; 87 | 88 | component VirtualFIFO is 89 | generic ( 90 | WORDSIZE : natural := 1 91 | ); 92 | port ( 93 | signal clk : in std_logic; 94 | signal wr_ready : out std_logic; 95 | signal rd_ready : out std_logic; 96 | signal wr_enable : in std_logic; 97 | signal rd_enable : in std_logic; 98 | signal data_out : in std_logic_vector(8*WORDSIZE-1 downto 0); 99 | signal data_in : out std_logic_vector(8*WORDSIZE-1 downto 0) 100 | ); 101 | end component; 102 | 103 | end package; 104 | 105 | --! \} 106 | 107 | package body ghpi_fifo is 108 | function fifo_thread_init(arg: string; wsize: integer) 109 | return integer is begin 110 | return sim_fifo_thread_init(arg & NUL, wsize); 111 | end fifo_thread_init; 112 | 113 | function sim_fifo_thread_init(arg: string; wsize: integer) 114 | return integer is begin 115 | assert false report "VHPI" severity failure; 116 | end sim_fifo_thread_init; 117 | 118 | procedure fifo_thread_exit is begin 119 | assert false report "VHPI" severity failure; 120 | end fifo_thread_exit; 121 | 122 | procedure fifo_io( 123 | data: inout fdata; 124 | flags: inout fifoflag_t 125 | ) is 126 | begin 127 | assert false report "VHPI" severity failure; 128 | end fifo_io; 129 | 130 | end package body; 131 | 132 | -------------------------------------------------------------------------------- /libnetpp.chdl: -------------------------------------------------------------------------------- 1 | --! \file libnetpp.vhdl Generated autowrapper library package 2 | -- /* ONLY EDIT THIS FILE IF IT HAS A CHDL EXTENSION !!! */ 3 | -- This file is generated from __FILE__ 4 | -- Modifications will be lost, please edit __FILE__ 5 | -- // This is a CPP template to create a VHDL file using the C preprocessor. 6 | -- // C style comments are stripped from the resulting file. 7 | -- (c) 2011-2018 Martin Strubel 8 | 9 | library ieee; 10 | use ieee.std_logic_1164.all; 11 | use ieee.numeric_std.all; -- Unsigned 12 | 13 | --! \brief GHPI netpp wrapper package 14 | --! Provides a few functions to access netpp master functionality from 15 | --! the simulation. 16 | 17 | 18 | package ghpi_netpp is 19 | 20 | --! \defgroup GlobalSignals Global Signals 21 | --! These are experimental signals 22 | 23 | --! \addtogroup GlobalSignals 24 | --! \{ 25 | 26 | ----- GLOBAL SIGNALS ----- 27 | 28 | --! Global throttle signal. If high, use as throttle signal 29 | --! to virtual FIFO implementations such as VirtualFX2Fifo. 30 | signal global_throttle : std_logic := '1'; 31 | 32 | --! Global number of wait cycles for some entities to sleep. 33 | signal global_waitcycles : natural := 50000; 34 | 35 | --! Global debugger clock in MHz 36 | signal global_dbgclk : std_logic := '0'; 37 | 38 | --! \} 39 | 40 | ----- NETPP SLAVE API ----- 41 | 42 | --! \addtogroup GHPI_Netpp 43 | --! \{ 44 | 45 | function netpp_init(name : string) 46 | return integer; 47 | 48 | --! Initializes the netpp slave structures for a simulation 49 | --! Allows to pass a port number 50 | function netpp_init(name : string; portnum : integer) 51 | return integer; 52 | 53 | 54 | ----- NETPP MASTER API ----- 55 | subtype token_t is integer; 56 | subtype netpphandle_t is integer; 57 | type handle_t is access integer; 58 | 59 | -- The function wrapper appends a \000 character 60 | --! Opens a netpp target somewhere on the network 61 | function device_open(id: string) 62 | return netpphandle_t; 63 | --! Retrieves the netpp token by name 64 | function device_gettoken(h: netpphandle_t; id: string) 65 | return token_t; 66 | 67 | --! \} 68 | 69 | --! \addtogroup VirtualRAM 70 | --! \{ 71 | 72 | ----- RAM API ----- 73 | subtype ram_port_t is unsigned(31 downto 0); 74 | subtype byte_t is unsigned(7 downto 0); 75 | subtype regaddr_t is unsigned(7 downto 0); 76 | type rambuf_t is access integer; 77 | 78 | --! Allocates a new virtual RAM 79 | --! @param size Size in words (not bytes) 80 | --! @param name A unique name, used for netpp export 81 | function ram_new(name : string; bits: integer; size : integer) return rambuf_t; 82 | 83 | --! \} 84 | 85 | --! \addtogroup VFifoAPI 86 | --! \{ 87 | 88 | ----- FIFO API ----- 89 | 90 | subtype fifoflag_t is unsigned(0 to 5); 91 | -- FIFO flag indices: 92 | constant FIFO_RX : natural := 0; --! in: Read advance, out: FIFO not empty 93 | constant FIFO_TX : natural := 1; --! in: Write advance, out: FIFO not full 94 | constant FIFO_RXE : natural := 2; --! out: LOW when FIFO almost not empty 95 | constant FIFO_TXF : natural := 3; --! out: LOW when FIFO almost full 96 | constant FIFO_OVR : natural := 4; --! out: overrun bit. Write 1 to clear. 97 | constant FIFO_UNR : natural := 5; --! out: underrun bit. W1C. 98 | 99 | -- FLAG assignments for FX2 emulation 100 | constant TX_PROG : natural := 0; 101 | constant TX_EMPTY : natural := 1; 102 | constant RX_FULL : natural := 2; 103 | 104 | -- BUS options: 105 | constant BUS_LOCAL : natural := 0; 106 | constant BUS_GLOBAL : natural := 1; 107 | 108 | --! Access type. These do not directly map into C struct pointers, 109 | --! see functions using duplexfifo_t_ghdl as arguments for example 110 | --! on how to access the handle in C. 111 | type duplexfifo_t is access integer; 112 | 113 | --! Allocates a new virtual FIFO 114 | --! @param name A unique name, used for netpp export 115 | --! @param size Size in words (not bytes) 116 | --! @param wordsize Word size (1 or 2) 117 | function fifo_new(name : string; size : integer; wordsize : integer) 118 | return duplexfifo_t; 119 | 120 | --! \} 121 | 122 | --! \addtogroup VBusAPI 123 | --! \{ 124 | ----- NETPP BUS API ----- 125 | 126 | type bus_t is access integer; 127 | --! Bus flags. 0: read, 1: write, 2: read ready 128 | subtype busflag_t is unsigned(0 to 2); 129 | 130 | --! Create a new virtual bus. 131 | --! @param name Name in the netpp root hierarchy 132 | --! @param wordsize Data word width in bytes 133 | function bus_new(name : string; wordsize : integer; bustype : integer) 134 | return bus_t; 135 | 136 | --! \} 137 | 138 | 139 | ----- NETPP REMOTE FRAMEBUFFER API ----- 140 | 141 | --! \addtogroup VFramebuf 142 | --! \{ 143 | 144 | subtype framebuffer_t is integer; --! Framebuffer handle 145 | subtype pixel_t is unsigned(15 downto 0); --! Pixel type 146 | type pixarray_t is array(natural range <>) of pixel_t; --! Pixel array 147 | 148 | constant VIDEOMODE_8BIT : natural := 1; --! 8 Bit grayscale 149 | constant VIDEOMODE_UYVY : natural := 3; --! UYVY 16 bpp mode 150 | constant VIDEOMODE_INDEXED : natural := 17; --! Indexed 16bit mode 151 | 152 | --! \} 153 | 154 | 155 | #include "func_decl.chdl" 156 | 157 | end package; 158 | 159 | 160 | package body ghpi_netpp is 161 | 162 | 163 | function netpp_init(name : string) 164 | return integer is 165 | begin 166 | return netpp_init_wrapped(name & NUL, 0); 167 | end function; 168 | 169 | function netpp_init(name : string; portnum : integer) 170 | return integer is 171 | begin 172 | return netpp_init_wrapped(name & NUL, portnum); 173 | end function; 174 | 175 | function device_open(id: string) 176 | return netpphandle_t is 177 | variable dev : netpphandle_t; 178 | begin 179 | dev := device_open_wrapped(id & NUL); 180 | if dev < 0 then 181 | assert false report "Failed to open netpp remote device" 182 | severity failure; 183 | end if; 184 | return dev; 185 | end device_open; 186 | 187 | function device_gettoken(h: netpphandle_t; id: string) 188 | return token_t is 189 | begin 190 | return device_gettoken_wrapped(h, id & NUL); 191 | end device_gettoken; 192 | 193 | -- Wrap functions with string parameter: Just append a null termination 194 | -- to become C standard compatible. 195 | 196 | function ram_new(name : string; bits: integer; size : integer) return rambuf_t is 197 | begin 198 | return ram_new_wrapped(name & NUL, bits, size); 199 | end function; 200 | 201 | function fifo_new(name : string; size : integer; wordsize : integer ) 202 | return duplexfifo_t is 203 | begin 204 | return fifo_new_wrapped(name & NUL, size, wordsize); 205 | end function; 206 | 207 | function bus_new(name : string; wordsize : integer; bustype : integer ) 208 | return bus_t is 209 | begin 210 | return bus_new_wrapped(name & NUL, wordsize, bustype); 211 | end function; 212 | 213 | #include "func_body.chdl" 214 | 215 | end package body; 216 | 217 | -------------------------------------------------------------------------------- /platform.mk: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -g 2 | 3 | PLATFORM_ARCH ?= $(shell uname) 4 | 5 | ifeq ($(PLATFORM_ARCH),Linux) 6 | CONFIG_LINUX = y 7 | DLLEXT = so 8 | endif 9 | 10 | ifeq ($(PLATFORM_ARCH),mingw32) 11 | MASOCIST = $(HOME)/src/vhdl/masocist 12 | CONFIG_MINGW32 = y 13 | DLLEXT = dll 14 | include $(MASOCIST)/vendor/section5/mingw32_config.mk 15 | LIBDIR=$(notdir $(GHDL)) 16 | CC=$(CROSS_CC) 17 | AR=$(CROSS_AR) 18 | RANLIB=$(CROSS_RANLIB) 19 | endif 20 | -------------------------------------------------------------------------------- /py/board.py: -------------------------------------------------------------------------------- 1 | #!/bin/env/python 2 | 3 | # Simple test access of netpp simulation server 4 | # 5 | # Run the board simulation 'simboard', then this script from the current 6 | # working directory. 7 | # 8 | # (c) 2010-2018 Martin Strubel 9 | 10 | import time 11 | import netpp 12 | from utils import * 13 | 14 | SIMULATION_URL = "TCP:localhost:2010" 15 | 16 | def test_blk(r, bufsize, throttle = 1, delay = 0.01): 17 | t = "" 18 | l = 0 19 | fifo = getattr(r, ":simboard:nfifo(0):fifo:") 20 | r.SimThrottle.set(throttle) 21 | 22 | COUNT_WRAP = 0x0100 23 | 24 | n = bufsize / 2 25 | 26 | should = "" 27 | c = 0 28 | for i in range(n): 29 | should += chr((c >> 8) & 0xff) + chr(c & 0xff) 30 | c += 1 31 | if c == COUNT_WRAP: 32 | c = 0 33 | 34 | b = buffer(should) 35 | fifo.Buffer.set(b) 36 | 37 | a = netpp.Buffer(bufsize) 38 | 39 | print "buf size: %d bytes, reference size: %d" % (bufsize, len(should)) 40 | 41 | 42 | retry = 0 43 | while l < bufsize: 44 | fill = fifo.InFill.get() 45 | print fill 46 | if fill >= bufsize: 47 | fifo.Buffer.get(a) 48 | # dump(a) 49 | t += str(a) 50 | l += len(a) 51 | retry = 0 52 | elif delay > 0.0: 53 | print "Polling... %.1f s (retry %d).." % (delay, retry) 54 | r.SimThrottle.set(0) 55 | time.sleep(delay) 56 | r.SimThrottle.set(throttle) 57 | retry += 1 58 | 59 | if retry > 10: 60 | print "Got nothing for 10 retries" 61 | break 62 | 63 | if len(t) > len(should): 64 | print "Truncated %d" % (len(t) - len(should)) 65 | t = t[:len(should)] 66 | 67 | if should != t: 68 | if len(t) != len(should): 69 | print "Length mismatch" 70 | else: 71 | i = find_mismatch(should, t) 72 | print "Position", i 73 | dump(should[i:i+16]) 74 | dump(t[i:i+16]) 75 | f = open("dump.bin", "w") 76 | f.write(t) 77 | f.close() 78 | else: 79 | print "Buffer ok!" 80 | 81 | def run_test(r): 82 | enable = r.Enable 83 | reset = r.Reset 84 | 85 | enable.set(0) 86 | reset.set(1) 87 | time.sleep(0.1) 88 | reset.set(0) 89 | 90 | enable.set(1) 91 | 92 | print "read n blocks, accelerated" 93 | for i in range(4): 94 | test_blk(r, 512, 1, 1.0) 95 | 96 | print "read n blocks, non-throttled" 97 | for i in range(4): 98 | test_blk(r, 512, 0, 0.0) 99 | 100 | return True 101 | 102 | if __name__ == "__main__": 103 | dev = netpp.connect(SIMULATION_URL) 104 | r = dev.sync() 105 | run_test(r) 106 | -------------------------------------------------------------------------------- /py/bus.py: -------------------------------------------------------------------------------- 1 | # Example to test virtual bus 2 | # 3 | 4 | 5 | import netpp 6 | from utils import * 7 | import sys 8 | 9 | def run_test(r): 10 | bus = r.TAP 11 | 12 | # Local bus 13 | 14 | buf = netpp.Buffer(32) 15 | r.localbus.Addr.set(0) 16 | 17 | r.localbus.DataBurst.get(buf) 18 | dump(buf) 19 | 20 | a = bus.Data.get() 21 | 22 | print "dumb method get:" 23 | r.localbus.Addr.set(0) 24 | b = r.localbus.DataBurst.get() 25 | dump(b) 26 | b = r.localbus.DataBurst.get() 27 | dump(b) 28 | b = r.localbus.DataBurst.get() 29 | dump(b) 30 | 31 | # Global bus (accessible through properties) 32 | r.SimThrottle.set(0) # Turn off Throttle 33 | a = r.SimThrottle.get() # Dummy read to make sure the SimThrottle/off is 34 | # effective before we continue 35 | 36 | # Dump the first 8 addresses: 37 | for i in range(8): 38 | bus.Addr.set(i) 39 | print "Data [%d]: %08x" % (i, bus.Data.get() & 0xffffffff) 40 | 41 | r.SimThrottle.set(1) # Resume Throttle 42 | a = r.SimThrottle.get() # Dummy read to make sure the SimThrottle/off is 43 | 44 | 45 | # The local bus can only be accessed directly, no netpp properties 46 | # map to it. 47 | r.localbus.Addr.set(0) 48 | magic = r.localbus.Data.get() 49 | if magic != 0xbaadf00d: 50 | print "WARNING: Unsigned integer return. FIXME (Python API)." 51 | 52 | if magic & 0xffffffff != 0xbaadf00d: 53 | print hex(magic) 54 | raise ValueError, "Failed to read magic from local bus" 55 | 56 | return True 57 | 58 | 59 | SIMULATION_URL = "TCP:localhost:2010" 60 | 61 | if __name__ == "__main__": 62 | dev = netpp.connect(SIMULATION_URL) 63 | r = dev.sync() 64 | run_test(r) 65 | -------------------------------------------------------------------------------- /py/fifo.py: -------------------------------------------------------------------------------- 1 | #!/bin/env/python 2 | 3 | # Simple test access of netpp simulation server 4 | # (c) 2010-2018 Martin Strubel 5 | 6 | import time 7 | import netpp 8 | 9 | SIMULATION_URL = "TCP:localhost:2010" 10 | 11 | 12 | def seq2buf(seq): 13 | b = "" 14 | for i in seq: 15 | b += chr(i) 16 | 17 | return buffer(b) 18 | 19 | def dump(seq): 20 | c = 0 21 | for i in seq: 22 | print "%02x" % (ord(i)), 23 | if c == 16: 24 | c = 0 25 | print 26 | print 27 | 28 | 29 | dev = netpp.connect(SIMULATION_URL) 30 | r = dev.sync() 31 | f = getattr(r, ':simboard:nfifo(0):fifo:') 32 | fifo = f.Buffer 33 | enable = r.Enable 34 | 35 | seq = [ 36 | 0x12, 0x20, 0x40, 0x00, 37 | ] 38 | 39 | buf = seq2buf(seq) 40 | 41 | r.SimThrottle.set(0) # No slow down 42 | fifo.set(buf) 43 | time.sleep(0.1) 44 | enable.set(1) 45 | 46 | time.sleep(0.5) 47 | a = fifo.get() 48 | dump(a) 49 | r.SimThrottle.set(1) # Slow down simulation when FIFO is idle 50 | -------------------------------------------------------------------------------- /py/ram.py: -------------------------------------------------------------------------------- 1 | import netpp 2 | import struct 3 | 4 | ENDIAN = ">" 5 | 6 | def get_dword(buf, i): 7 | i *= 4 8 | a = buf[i:i+4] 9 | s = struct.unpack(ENDIAN + "L", a) 10 | 11 | return s[0] 12 | 13 | def get_hword(buf, i): 14 | i *= 2 15 | a = buf[i:i+2] 16 | s = struct.unpack(ENDIAN + "H", a) 17 | 18 | return s[0] 19 | 20 | 21 | def run_test(r): 22 | # We need to get the attribute manually, 23 | # because it has a non-pythonish namespace id: 24 | ram0 = getattr(r, ":simboard:ram0:") 25 | ram1 = getattr(r, ":simboard:ram1:wrapper:") 26 | ram2 = r.Shadow32bit 27 | 28 | ram0.Offset.set(0) 29 | ram1.Offset.set(0) 30 | ram2.Offset.set(0) 31 | 32 | retry = 5 33 | 34 | # We have a few retries 35 | 36 | while r.Break.get() == 0 and retry > 0: 37 | retry -= -1 38 | 39 | if r.Break.get() == 0: 40 | raise SystemError, "Simulation not ready" 41 | 42 | a = ram0.Buffer.get() 43 | b = ram1.Buffer.get() 44 | c = ram2.Buffer.get() 45 | 46 | w = get_dword(c, 1) 47 | 48 | if w == 0xdeadbeef: 49 | print("Correct big endian initialization of 32 bit RAM") 50 | retry = 0 51 | elif w == 0xefbeadde: 52 | raise ValueError, "Wrong (legacy) little endian initialization of 32 bit RAM" 53 | else: 54 | raise ValueError, "Init value mismatch. Maybe restart simulation?" 55 | 56 | 57 | wl = get_hword(a, 1) 58 | wh = get_hword(b, 1) 59 | 60 | if wh == 0xdead and wl == 0xbeef: 61 | print("RAM0 and RAM1 LO/HI contents match") 62 | else: 63 | raise ValueError, "RAM error" 64 | 65 | 66 | 67 | c[0] = 'c' 68 | c[1] = 'o' 69 | c[2] = 'd' 70 | c[3] = 'e' 71 | 72 | ram2.Buffer.set(c) 73 | c1 = ram2.Buffer.get() 74 | 75 | if c1 != c: 76 | raise ValueError, "RAM contents do not match" 77 | 78 | ram2.Offset.set(4) 79 | c2 = ram2.Buffer.get() 80 | 81 | if c2[0] != c[4]: 82 | raise ValueError, "Offset test failed" 83 | 84 | return True 85 | 86 | 87 | if __name__ == "__main__": 88 | dev = netpp.connect("localhost:2010") 89 | r = dev.sync() 90 | 91 | run_test(r) 92 | -------------------------------------------------------------------------------- /py/test.py: -------------------------------------------------------------------------------- 1 | #!/bin/env/python 2 | 3 | # Simple test access of netpp simulation server 4 | # 5 | # Run the board simulation 'simboard', then this script from the current 6 | # working directory. 7 | # 8 | # (c) 2010-2018 Martin Strubel 9 | 10 | import time 11 | import netpp 12 | 13 | SIMULATION_URL = "TCP:localhost:2010" 14 | 15 | def dump(seq): 16 | c = 0 17 | for i in seq: 18 | print "%02x" % (ord(i)), 19 | c += 1 20 | if c == 16: 21 | c = 0 22 | print 23 | print 24 | 25 | def find_mismatch(b0, b1): 26 | for i in range(len(b0)): 27 | if b0[i] != b1[i]: 28 | print "Mismatch from %d" % i, 29 | print "should be: %02x, is: %02x" % (ord(b0[i]), ord(b1[i])) 30 | break 31 | return i 32 | 33 | def test_blk(r, bufsize, throttle = 1, delay = 0.01): 34 | t = "" 35 | l = 0 36 | fifo = getattr(r, ":simboard:nfifo(0):fifo:") 37 | r.SimThrottle.set(throttle) 38 | 39 | COUNT_WRAP = 0x0100 40 | 41 | n = bufsize / 2 42 | 43 | should = "" 44 | c = 0 45 | for i in range(n): 46 | should += chr((c >> 8) & 0xff) + chr(c & 0xff) 47 | c += 1 48 | if c == COUNT_WRAP: 49 | c = 0 50 | 51 | b = buffer(should) 52 | fifo.Buffer.set(b) 53 | 54 | a = netpp.Buffer(bufsize) 55 | 56 | print "buf size: %d bytes, reference size: %d" % (bufsize, len(should)) 57 | 58 | 59 | retry = 0 60 | while l < bufsize: 61 | fill = fifo.InFill.get() 62 | print fill 63 | if fill >= bufsize: 64 | fifo.Buffer.get(a) 65 | # dump(a) 66 | t += str(a) 67 | l += len(a) 68 | retry = 0 69 | elif delay > 0.0: 70 | print "Polling... %.1f s (retry %d).." % (delay, retry) 71 | r.SimThrottle.set(0) 72 | time.sleep(delay) 73 | r.SimThrottle.set(throttle) 74 | retry += 1 75 | 76 | if retry > 10: 77 | print "Got nothing for 10 retries" 78 | break 79 | 80 | if len(t) > len(should): 81 | print "Truncated %d" % (len(t) - len(should)) 82 | t = t[:len(should)] 83 | 84 | if should != t: 85 | if len(t) != len(should): 86 | print "Length mismatch" 87 | else: 88 | i = find_mismatch(should, t) 89 | print "Position", i 90 | dump(should[i:i+16]) 91 | dump(t[i:i+16]) 92 | f = open("dump.bin", "w") 93 | f.write(t) 94 | f.close() 95 | else: 96 | print "Buffer ok!" 97 | 98 | 99 | dev = netpp.connect(SIMULATION_URL) 100 | 101 | r = dev.sync() 102 | enable = r.Enable 103 | reset = r.Reset 104 | 105 | enable.set(0) 106 | reset.set(1) 107 | time.sleep(0.1) 108 | reset.set(0) 109 | 110 | enable.set(1) 111 | 112 | print "read n blocks, accelerated" 113 | for i in range(4): 114 | test_blk(r, 512, 1, 1.0) 115 | 116 | print "read n blocks, non-throttled" 117 | for i in range(4): 118 | test_blk(r, 512, 0, 0.0) 119 | 120 | fifo = getattr(r, ":simboard:nfifo(0):fifo:") 121 | 122 | print 80 * "-" 123 | print "Flush..." 124 | while fifo.InFill.get() > 128: 125 | test_blk(r, 128, 1, 0.1) 126 | 127 | # Let FIFO flood: 128 | 129 | r.SimThrottle.set(0) 130 | time.sleep(0.2) 131 | r.SimThrottle.set(1) 132 | 133 | print "See if more data coming..." 134 | try: 135 | c = fifo.Buffer.get() 136 | print "Had left in buffer:", len(c) 137 | except TypeError: 138 | print "Empty buffer" 139 | 140 | -------------------------------------------------------------------------------- /py/test_board.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import time 5 | import netpp 6 | import pytest 7 | import subprocess 8 | 9 | import sys 10 | sys.path.append("py") 11 | import board 12 | import ram 13 | import bus 14 | 15 | 16 | SLAVE = "./simboard" 17 | 18 | @pytest.yield_fixture(autouse=True, scope='session') 19 | def test_cleanup(): 20 | global slave, dev, root 21 | slave = subprocess.Popen([SLAVE]) 22 | time.sleep(0.5) 23 | dev = netpp.connect("TCP:localhost:2010") 24 | root = dev.sync() 25 | yield 26 | slave.terminate() 27 | 28 | def test_board_ram(): 29 | assert ram.run_test(root) 30 | 31 | def test_board_bus(): 32 | assert bus.run_test(root) 33 | 34 | def test_board_fifo(): 35 | assert board.run_test(root) 36 | 37 | 38 | -------------------------------------------------------------------------------- /py/test_client.py: -------------------------------------------------------------------------------- 1 | 2 | import sys 3 | import os 4 | import time 5 | import netpp 6 | import pytest 7 | import subprocess 8 | 9 | 10 | NETPP = os.getenv("NETPP") 11 | 12 | if not NETPP: 13 | NETPP = "/usr/share/netpp" 14 | 15 | SLAVE = NETPP + "/devices/example/slave" 16 | 17 | @pytest.yield_fixture(autouse=True, scope='session') 18 | def test_cleanup(): 19 | global client 20 | server = subprocess.Popen([SLAVE, "--port=2008"]) 21 | time.sleep(0.5) 22 | yield 23 | server.terminate() 24 | 25 | def test_client(): 26 | client = subprocess.Popen(["./simnetpp"]) 27 | ret = client.wait() 28 | assert ret == 0 29 | -------------------------------------------------------------------------------- /py/utils.py: -------------------------------------------------------------------------------- 1 | 2 | def dump(seq): 3 | c = 0 4 | for i in seq: 5 | print "%02x" % (ord(i)), 6 | c += 1 7 | if c == 16: 8 | c = 0 9 | print 10 | print 11 | 12 | def find_mismatch(b0, b1): 13 | for i in range(len(b0)): 14 | if b0[i] != b1[i]: 15 | print "Mismatch from %d" % i, 16 | print "should be: %02x, is: %02x" % (ord(b0[i]), ord(b1[i])) 17 | break 18 | return i 19 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # Rules to build ghdlex library objects: 2 | # 3 | 4 | DEVICEFILE = ../ghdlsim.xml 5 | 6 | GHDLEX = .. 7 | 8 | RANLIB ?= ranlib 9 | 10 | CONFIG_NETPP = $(shell [ -e $(NETPP)/xml ] && echo y ) 11 | 12 | include ../platform.mk 13 | 14 | # Special case: Build static for mingw32 cross: 15 | DUTIES-$(CONFIG_MINGW32) = $(LIBSIM).a 16 | DUTIES-$(CONFIG_LINUX) += $(LIBSIM).$(DLLEXT) 17 | DUTIES-$(CONFIG_NETPP_VPI) += netpp.vpi 18 | 19 | 20 | 21 | all: $(DUTIES-y) 22 | 23 | VPIOBJS = vpiwrapper.o 24 | VPIOBJS += vpi_proplist.o vpi_netpp.o vpi_ram.o vpi_bus.o vpi_handler.o 25 | 26 | include project.mk 27 | 28 | OBJS = $(CSRCS:%.c=%.o) 29 | 30 | ifeq ($(CONFIG_NETPP),y) 31 | OBJS += proplist.o 32 | endif 33 | 34 | ifeq ($(CONFIG_NETPP),y) 35 | include $(NETPP)/xml/prophandler.mk 36 | endif 37 | 38 | regprops.xml: $(DEVICEFILE) $(XSLT)/regwrap.xsl 39 | $(XP) -o $@ \ 40 | --stringparam selectDevice ghdlsim \ 41 | $(XSLT)/regwrap.xsl $< 42 | 43 | registermap.h: $(DEVICEFILE) 44 | $(XP) -o $@ --stringparam srcfile $< \ 45 | --param convertBitfields 1 \ 46 | --param useMapPrefix 1 \ 47 | --stringparam regprefix R_ \ 48 | $(XSLT)/registermap.xsl $< 49 | 50 | vpiwrapper.o: vpiwrapper.c 51 | $(CC) -o $@ -c $(CFLAGS) $< 52 | 53 | vpi_%.o: %.c 54 | $(CC) -o $@ -c $(CFLAGS) $< 55 | 56 | # Only with netpp > v0.4 57 | netpp.vpi: $(VPIOBJS) 58 | $(CC) -shared -o $@ $(VPIOBJS) -lpthread -L$(LIBSLAVE) -lslave 59 | 60 | handler.c: registermap.h 61 | 62 | $(LIBSIM).a: $(OBJS) 63 | $(AR) ruv $@ $(OBJS) 64 | $(RANLIB) $@ 65 | 66 | $(LIBSIM).so: $(OBJS) 67 | $(CC) -shared -o $@ $(OBJS) -lpthread 68 | 69 | $(LIBSIM).dll: $(OBJS) 70 | $(CC) -shared -o $@ $(OBJS) 71 | 72 | clean:: 73 | rm -f *.o *.a $(DUTIES-y) 74 | 75 | 76 | show-duties: 77 | @echo $(DUTIES-y) 78 | -------------------------------------------------------------------------------- /src/apimacros.h: -------------------------------------------------------------------------------- 1 | /** \file apimacros.h 2 | * 3 | * \brief Nasty macros for multiple path definition expansion between 4 | * C and GHDL interface 5 | * 6 | * (c) 2011-2013, Martin Strubel 7 | */ 8 | 9 | #ifdef APIDEF_UNINITIALIZE 10 | # undef VHDL_COMMENT 11 | # undef _T 12 | # undef API_DEF 13 | # undef ARG 14 | # undef ARGO 15 | # undef ARGIO 16 | # undef ARGIOP 17 | # undef DEFTYPE_EXPLICIT 18 | # undef DEFTYPE_PROTOSTRUCT 19 | # undef DEFTYPE_SLV 20 | #else 21 | 22 | // Run the actual definition: 23 | 24 | #define SHIFT_ARGS(x, ...) __VA_ARGS__ 25 | 26 | #if defined(RUN_TYPES) 27 | # define VHDL_COMMENT(x) 28 | # ifdef DEBUG 29 | # warning "Running Type generation mode" 30 | # endif 31 | # define _T(t) s_vhdl_type_##t 32 | # define DEFTYPE_EXPLICIT(t, def) \ 33 | static const char _T(t)[] = #t; 34 | 35 | #define DEFTYPE_PROTOSTRUCT(t, def) \ 36 | static const char _T(t)[] = #t; 37 | # define DEFTYPE_SLV(t, s) \ 38 | static const char _T(t)[] = #t; 39 | # define API_DEF(t, nm, ret, ...) 40 | #elif defined(RUN_CHEAD) 41 | # ifdef DEBUG 42 | # warning "Running C header mode" 43 | # endif 44 | 45 | // This section contains the generated Doxygen documentation 46 | 47 | // Hack: We have to guard comments using this define, otherwise 48 | // the documentation turns up in doc_apidef.h 49 | #ifndef NO_MACRO_DOCS 50 | 51 | /** \defgroup Macros Macro documentation 52 | */ 53 | 54 | /** \addtogroup Macros 55 | * \brief Internal macros 56 | * 57 | * These macros are used in apidef.h 58 | * 59 | * \{ */ 60 | 61 | 62 | /** Add VHDL comment. This is translated into the VHDL sources 63 | * run through doxygen 64 | */ 65 | #endif // NO_MACRO_DOCS 66 | # define VHDL_COMMENT(x) 67 | # define _T(t) t##_ghdl 68 | #ifndef NO_MACRO_DOCS 69 | /** Creates an explicit typedef for the specified VHDL data type. 70 | * The VHDL data type has a '_ghdl' suffix on the C side. See also 71 | * \ref GHPI_Wrap 72 | * \param t Type name 73 | * \param def C definition 74 | */ 75 | #endif 76 | # define DEFTYPE_EXPLICIT(t, def) \ 77 | typedef def _T(t); 78 | #ifndef NO_MACRO_DOCS 79 | /** Macro to define another data proxy type for both C and VHDL side 80 | * \param t The VHDL data type (must be defined in libnetpp.chdl 81 | * or elsewhere 82 | * \param def The corresponding C data type (defined externally) 83 | */ 84 | #endif 85 | # define DEFTYPE_PROTOSTRUCT(t, def) \ 86 | def; typedef def * _T(t); 87 | #ifndef NO_MACRO_DOCS 88 | /** Creates VHDL typedef for a std_logic_vector 89 | * \param t Type name 90 | * \param s Size of vector in bits 91 | */ 92 | #endif 93 | # define DEFTYPE_SLV(t, s) \ 94 | typedef char _T(t)[s]; 95 | # define API_DEF(t, nm, ret, ...) \ 96 | ret sim_##nm(__VA_ARGS__); 97 | # define ARG(n, t) _T(t) n 98 | # define ARGO(n, t) ARG(n, t) 99 | # define ARGIO(n, t) ARG(n, t) 100 | # define ARGIOP(n, t) ARG(*n, t) 101 | 102 | 103 | #ifndef NO_MACRO_DOCS 104 | /** \} */ 105 | #endif 106 | 107 | #else 108 | # ifdef DEBUG 109 | # warning "Running VHDL mode" 110 | # endif 111 | # define VHDL_COMMENT(x) { .type = TYPE_COMMENT, .name = x }, 112 | # define _T(t) s_vhdl_type_##t 113 | # define DEFTYPE_EXPLICIT(t, def) 114 | # define DEFTYPE_PROTOSTRUCT(t, def) 115 | # define DEFTYPE_SLV(t, s) 116 | # define ARG(n, t) #n, _T(t), 0 117 | # define ARGO(n, t) #n, _T(t), "out" 118 | # define ARGIO(n, t) #n, _T(t), "inout" 119 | # define ARGIOP(n, t) #n, _T(t), "inout" 120 | # define API_DEF(t, nm, ret, ...) \ 121 | { .type = t, .name = #nm, .retitem = ret, \ 122 | .parameters = { __VA_ARGS__, NULL }}, 123 | 124 | #endif 125 | 126 | #define API_DEFFUNC(...) API_DEF(TYPE_FUNC, __VA_ARGS__) 127 | #define API_DEFPROC(...) API_DEF(TYPE_PROC, __VA_ARGS__) 128 | 129 | 130 | #define DEFTYPE_HANDLE32(t) DEFTYPE_EXPLICIT(t, uint32_t) 131 | #define DEFTYPE_FATP(t) DEFTYPE_EXPLICIT(t, struct fat_pointer *) 132 | 133 | # define ARG_O(n, t) _T(t) n 134 | 135 | #endif // UNINITIALIZE 136 | -------------------------------------------------------------------------------- /src/bus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "bus.h" 3 | #include "ghpi.h" 4 | #include "netpp.h" 5 | #include "netppwrap.h" 6 | #include "property_protocol.h" // netpp_log 7 | 8 | extern Bus *g_bus; 9 | 10 | uint32_t g_vbus_addr; 11 | 12 | bus_t_ghdl sim_bus_new_wrapped(string_ghdl name, integer_ghdl width, 13 | integer_ghdl type) 14 | { 15 | int error; 16 | char propname[32]; 17 | 18 | Bus *b = 19 | (Bus *) malloc(sizeof(Bus) + BUS_AUXBUFSIZE); 20 | netpp_log(DCLOG_NOTICE, "Reserved Bus '%s' with word size %d", (char *) name->base, 21 | width); 22 | MUTEX_INIT(&b->mutex); 23 | b->width = (width + 7) / 8; // Convert to byte width and remember 24 | b->flags = 0; 25 | b->timeout_ms = 3000; 26 | b->tmpbuf = (char *) &b[1]; 27 | b->bufsize = BUS_AUXBUFSIZE; 28 | 29 | switch (type) { 30 | case 1: 31 | if (g_bus) { 32 | netpp_log(DCLOG_ERROR, 33 | "You can only have one global bus (property-accessible)\n" 34 | "Overriding previous global bus."); 35 | } 36 | g_bus = b; 37 | } 38 | 39 | ghdlname_to_propname(name->base, propname, sizeof(propname)); 40 | error = register_bus(b, propname); 41 | if (error < 0) return 0; 42 | 43 | return (bus_t_ghdl) b; 44 | } 45 | 46 | void_ghdl sim_bus_del(bus_t_ghdl *bus) 47 | { 48 | // FIXME: Should unregister property, but simulation always 49 | // should exit upon this call 50 | free(bus); 51 | } 52 | 53 | 54 | void sim_bus_rxtx(bus_t_ghdl *bus, unsigned_ghdl addr, unsigned_ghdl data, 55 | char *flag) 56 | { 57 | Bus *b = (Bus *) *bus; 58 | if (!b) { 59 | netpp_log(DCLOG_ERROR, "Bus not initialized, is NULL"); 60 | exit(-1); 61 | } 62 | 63 | MUTEX_LOCK(&b->mutex); 64 | 65 | if (b->flags & TX_PEND) { 66 | flag[1] = HIGH; b->flags &= ~TX_PEND; 67 | uint_to_logic(data->base, data->bounds->len, b->data); 68 | uint_to_logic(addr->base, addr->bounds->len, b->addr); 69 | } else { 70 | flag[1] = LOW; 71 | } 72 | 73 | if ((b->flags & (RX_BUSY | RX_PEND)) == RX_PEND) { 74 | flag[0] = HIGH; 75 | b->flags |= RX_BUSY; 76 | uint_to_logic(addr->base, addr->bounds->len, b->addr); 77 | } 78 | else { flag[0] = LOW; } 79 | 80 | // Data ready? 81 | if (flag[2] == HIGH) { 82 | logic_to_uint(data->base, data->bounds->len, &b->data); 83 | b->flags &= ~(RX_PEND); 84 | flag[2] = LOW; 85 | } 86 | 87 | MUTEX_UNLOCK(&b->mutex); 88 | } 89 | 90 | /* Cosimulation side bus write */ 91 | int bus_val_wr(Bus *bus, uint32_t addr, uint32_t val) 92 | { 93 | int error = 0; 94 | // Wait until slave has read previous data sent 95 | int retry = 0; 96 | while (bus->flags & TX_PEND) { 97 | netpp_log(DCLOG_NOTICE, "Poll until slave ready"); 98 | USLEEP(1000); // Wait 1 ms 99 | retry++; 100 | if (retry > bus->timeout_ms) { 101 | netpp_log(DCLOG_ERROR, "Bus timeout. No response from Simulation?"); 102 | return DCERR_COMM_TIMEOUT; 103 | } 104 | } 105 | netpp_log(DCLOG_NOTICE, "%08x> %08x", addr, val); 106 | 107 | MUTEX_LOCK(&bus->mutex); 108 | bus->addr = addr; 109 | bus->data = val; 110 | bus->flags |= TX_PEND; 111 | MUTEX_UNLOCK(&bus->mutex); 112 | // Wait for the simulation thread to actually commit the data: 113 | return error; 114 | } 115 | 116 | int bus_val_rd(Bus *bus, uint32_t addr, uint32_t *val) 117 | { 118 | MUTEX_LOCK(&bus->mutex); 119 | bus->addr = addr; 120 | bus->flags |= RX_PEND; 121 | MUTEX_UNLOCK(&bus->mutex); 122 | int retry = 0; 123 | while ((bus->flags & (RX_PEND))) { 124 | // printf("Poll read...\n"); 125 | USLEEP(1000); 126 | retry++; 127 | if (retry > bus->timeout_ms) { 128 | netpp_log(DCLOG_ERROR, "Bus timeout. No response from Simulation?"); 129 | return DCERR_COMM_TIMEOUT; 130 | } 131 | 132 | } 133 | MUTEX_LOCK(&bus->mutex); 134 | bus->flags &= ~RX_BUSY; 135 | MUTEX_UNLOCK(&bus->mutex); 136 | 137 | *val = bus->data; 138 | return 0; 139 | } 140 | 141 | //////////////////////////////////////////////////////////////////////////// 142 | // LITTLE ENDIAN ACCESS 143 | 144 | static 145 | int bus_write_le(Bus *bus, const uint8_t *buf, int size) 146 | { 147 | uint32_t val; 148 | uint32_t addr; 149 | int n, k; 150 | int s; 151 | val = 0; 152 | int error = 0; 153 | 154 | const uint8_t *end = &buf[size]; 155 | 156 | k = size % bus->width; 157 | 158 | if (k) end -= k; 159 | 160 | addr = bus->addr; 161 | 162 | while (buf < end) { 163 | n = bus->width; 164 | 165 | // Little endian conversion: 166 | s = 0; val = 0; 167 | while (n--) { 168 | val |= (*buf++) << s; s += 8; 169 | } 170 | error = bus_val_wr(bus, addr, val); 171 | if (error < 0) return error; 172 | addr += bus->width; 173 | } 174 | 175 | if (k) { 176 | s = 0; val = 0; 177 | while (k--) { 178 | val |= (*buf++) << s; s += 8; 179 | } 180 | error = bus_val_wr(bus, addr, val); 181 | addr += bus->width; 182 | } 183 | 184 | // Wait for last write to finish: 185 | while (bus->flags & TX_PEND); 186 | 187 | MUTEX_LOCK(&bus->mutex); 188 | bus->addr = addr; // Store incremented address for subsequent writes 189 | MUTEX_UNLOCK(&bus->mutex); 190 | return error; 191 | } 192 | 193 | 194 | static 195 | int bus_read_le(Bus *bus, uint8_t *buf, int size) 196 | { 197 | uint32_t val; 198 | uint32_t addr; 199 | int error; 200 | while ((bus->flags & (TX_PEND))) USLEEP(1000); 201 | int n, k; 202 | val = 0; 203 | 204 | k = size % bus->width; 205 | 206 | uint8_t *end = &buf[size]; 207 | if (k) end -= k; 208 | 209 | 210 | addr = bus->addr; 211 | 212 | while (buf < end) { 213 | error = bus_val_rd(bus, addr, &val); 214 | if (error < 0) return error; 215 | n = bus->width; 216 | // Store in little endian order: 217 | while (n--) { 218 | *buf++ = val; 219 | val >>= 8; 220 | } 221 | addr += bus->width; 222 | } 223 | 224 | // Get remainder: 225 | if (k) { 226 | error = bus_val_rd(bus, addr, &val); 227 | while (k--) { 228 | *buf++ = val; 229 | val >>= 8; 230 | } 231 | addr += bus->width; 232 | } 233 | 234 | MUTEX_LOCK(&bus->mutex); 235 | bus->addr = addr; // Store incremented address for subsequent reads 236 | MUTEX_UNLOCK(&bus->mutex); 237 | return error; 238 | } 239 | 240 | 241 | //////////////////////////////////////////////////////////////////////////// 242 | // BIG ENDIAN ACCESS 243 | 244 | static 245 | int bus_write_be(Bus *bus, const uint8_t *buf, int size) 246 | { 247 | uint32_t val; 248 | uint32_t addr; 249 | int n, k; 250 | val = 0; 251 | int error = 0; 252 | 253 | const uint8_t *end = &buf[size]; 254 | 255 | k = size % bus->width; 256 | 257 | if (k) end -= k; 258 | 259 | addr = bus->addr; 260 | 261 | while (buf < end) { 262 | n = bus->width; 263 | 264 | // Big endian conversion: 265 | val = 0; 266 | while (n--) { 267 | val <<= 8; 268 | val |= *buf++; 269 | } 270 | error = bus_val_wr(bus, addr, val); 271 | if (error < 0) return error; 272 | addr += bus->width; 273 | } 274 | 275 | if (k) { 276 | val = 0; 277 | while (k--) { 278 | val <<= 8; 279 | val |= *buf++; 280 | } 281 | error = bus_val_wr(bus, addr, val); 282 | addr += bus->width; 283 | } 284 | 285 | // Wait for last write to finish: 286 | while (bus->flags & TX_PEND); 287 | 288 | MUTEX_LOCK(&bus->mutex); 289 | bus->addr = addr; // Store incremented address for subsequent writes 290 | MUTEX_UNLOCK(&bus->mutex); 291 | return error; 292 | } 293 | 294 | 295 | static 296 | int bus_read_be(Bus *bus, uint8_t *buf, int size) 297 | { 298 | uint32_t val; 299 | uint32_t addr; 300 | uint8_t *b; 301 | int error; 302 | while ((bus->flags & (TX_PEND))) USLEEP(1000); 303 | int n, k; 304 | val = 0; 305 | 306 | k = size % bus->width; 307 | 308 | uint8_t *end = &buf[size]; 309 | if (k) end -= k; 310 | 311 | addr = bus->addr; 312 | 313 | while (buf < end) { 314 | error = bus_val_rd(bus, addr, &val); 315 | if (error < 0) return error; 316 | n = bus->width; 317 | // Store in big endian order: 318 | while (n--) { 319 | buf[n] = val; 320 | val >>= 8; 321 | } 322 | buf += bus->width; 323 | addr += bus->width; 324 | } 325 | 326 | // Get remainder: 327 | if (k) { 328 | error = bus_val_rd(bus, addr, &val); 329 | while (k--) { 330 | buf[k] = val; 331 | val >>= 8; 332 | } 333 | addr += bus->width; 334 | } 335 | 336 | MUTEX_LOCK(&bus->mutex); 337 | bus->addr = addr; // Store incremented address for subsequent reads 338 | MUTEX_UNLOCK(&bus->mutex); 339 | return error; 340 | } 341 | 342 | 343 | int bus_write(Bus *bus, const uint8_t *buf, int size) 344 | { 345 | int ret; 346 | if (bus->flags & BUS_LE) { 347 | ret = bus_write_le(bus, buf, size); 348 | } else { 349 | ret = bus_write_be(bus, buf, size); 350 | } 351 | return ret; 352 | } 353 | 354 | int bus_read(Bus *bus, uint8_t *buf, int size) 355 | { 356 | int ret; 357 | if (bus->flags & BUS_LE) { 358 | ret = bus_read_le(bus, buf, size); 359 | } else { 360 | ret = bus_read_be(bus, buf, size); 361 | } 362 | return ret; 363 | } 364 | -------------------------------------------------------------------------------- /src/bus.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * 3 | * \brief Virtual bus structure 4 | * 5 | */ 6 | 7 | #include 8 | #include "threadaux.h" 9 | 10 | /** \defgroup BUS Exported virtual Bus 11 | * 12 | * These functions are typically used by a handler function like 13 | * device_read() or device_write() inside the netpp wrapper. 14 | */ 15 | 16 | /** \addtogroup BUS 17 | * \{ 18 | */ 19 | 20 | #ifdef SUPPORT_LEGACY_REGISTERMAP 21 | #define VBUS_ADDR_OFFSET 0x100 ///< Virtual bus address offset 22 | #endif 23 | 24 | /** The internal bus structure */ 25 | typedef 26 | struct bus_t { 27 | uint32_t addr; ///< Address 28 | uint32_t data; ///< Data 29 | int width; ///< Bus width (Bytes) 30 | volatile char flags; ///< RX/TX software flags 31 | MUTEX mutex; // I/O locking for concurrent threads 32 | uint32_t timeout_ms; ///< Bus timeout in ms 33 | unsigned char *tmpbuf; // Buffer for I/O transaction 34 | uint32_t bufsize; // Buffer size 35 | } Bus; 36 | 37 | #define RX_BUSY 0x01 38 | #define RX_PEND 0x02 39 | #define TX_PEND 0x04 40 | #define BUS_LE 0x10 // Little endian 41 | 42 | #define BUS_AUXBUFSIZE 0x100 43 | 44 | /** Bus reading function */ 45 | int bus_read(Bus *bus, unsigned char *buf, int size); 46 | 47 | /** Bus write function */ 48 | int bus_write(Bus *bus, const unsigned char *buf, int size); 49 | 50 | /** Bus single value read */ 51 | int bus_val_rd(Bus *bus, uint32_t addr, uint32_t *val); 52 | /** Bus single value write */ 53 | int bus_val_wr(Bus *bus, uint32_t addr, uint32_t val); 54 | 55 | /** \} */ 56 | -------------------------------------------------------------------------------- /src/example.h: -------------------------------------------------------------------------------- 1 | /** Example implementation of direct variable access via the netpp 2 | * property mechanisms. 3 | */ 4 | 5 | /** Define this, when you wish to use the derived class 6 | * with experimental code. */ 7 | 8 | #define EXPERIMENTAL 9 | 10 | #ifdef EXPERIMENTAL 11 | # define DEVICE_INDEX 1 12 | #else 13 | # define DEVICE_INDEX 0 14 | #endif 15 | 16 | #define BUFSIZE 0x400 17 | #define STRINGSIZE 64 18 | 19 | void init_registermap(void); 20 | 21 | extern uint32_t g_vbus_addr; 22 | 23 | extern 24 | struct fifoconfig { 25 | int timeout; 26 | int retry; 27 | } g_fifoconfig; 28 | -------------------------------------------------------------------------------- /src/fifo.c: -------------------------------------------------------------------------------- 1 | /** \file fifo.c 2 | * 3 | * Stupid software FIFO for test purposes. 4 | * Update: not so stupid anymore. 5 | * 6 | * (c) 2009 Martin Strubel 7 | * 8 | * Changes: 9 | * 09/2011 Martin Strubel 10 | * Implemented a first-fall-through FIFO for the GHDL simulator 11 | * interface. 12 | * 10/2012 Martin Strubel 13 | * Added 16 bit option (wordsize parameter) 14 | * 15 | * The first version of this FIFO exhibited a pitfall: When burst 16 | * reading, the FIFO empty condition can not be handled in time. 17 | * Therefore, another flag RXAE is introduced. This flag goes 18 | * low when there is only one byte left to read from the FIFO. 19 | * Likewise, there is a TXAF flag. A sane VHDL implementation should 20 | * really check for both flags (we don't, in the provided example). 21 | * Things can go wrong if there is only ONE byte written to an empty FIFO. 22 | * 23 | */ 24 | 25 | #include // usleep() 26 | #include 27 | #include "property_protocol.h" // netpp_log 28 | #include "fifo.h" 29 | #include "ghpi.h" 30 | #include "netpp.h" 31 | #include "netppwrap.h" 32 | #include "example.h" 33 | #include "threadaux.h" 34 | 35 | 36 | #ifdef SUPPORT_LEGACY_FIFO 37 | #warning "Compiling with legacy FIFO code" 38 | extern struct duplexfifo_t g_dfifo; 39 | #endif 40 | 41 | int fifo_init(Fifo *f, unsigned short size, unsigned short wordsize) 42 | { 43 | int error; 44 | f->size = size; 45 | f->fromlogic = logic_to_bytes; 46 | switch (wordsize) { 47 | case 1: 48 | f->tologic = bytes_to_logic; 49 | break; 50 | case 2: 51 | f->tologic = words_to_logic; 52 | break; 53 | default: 54 | netpp_log(DCLOG_ERROR, "Unsupported word size"); 55 | return -1; 56 | } 57 | 58 | // netpp_log(DCLOG_VERBOSE, "Initialize FIFO with word width of %d bits", wordsize * 8); 59 | error = MUTEX_INIT(&f->mutex); 60 | if (error < 0) return error; 61 | f->buf = (unsigned char*) malloc(size * wordsize); 62 | if (!f->buf) return -1; 63 | f->head = 0; 64 | f->tail = 0; 65 | f->fill = 0; 66 | f->ovr = LOW; 67 | f->unr = LOW; 68 | return 0; 69 | } 70 | 71 | void fifo_exit(Fifo *f) 72 | { 73 | MUTEX_EXIT(&f->mutex); 74 | free(f->buf); 75 | } 76 | 77 | int fifo_getbyte(Fifo *f, unsigned char *byte) 78 | { 79 | if (f->fill == 0) { 80 | return 0; 81 | } 82 | *byte = f->buf[f->tail]; 83 | return 1; 84 | } 85 | 86 | static 87 | int fifo_get(Fifo *f, unsigned char *dst, int n) 88 | { 89 | unsigned short p; 90 | 91 | int ret = f->fill; 92 | 93 | p = f->tail; 94 | do { 95 | *dst++ = f->buf[p++]; 96 | p %= f->size; 97 | n--; 98 | } while (p != f->head && n > 0); 99 | 100 | return ret; 101 | } 102 | 103 | int fifo_advance(Fifo *f, int n) 104 | { 105 | int ret = 0; 106 | 107 | MUTEX_LOCK(&f->mutex); 108 | 109 | if (f->fill == 0) { 110 | f->unr = HIGH; 111 | netpp_log(DCLOG_ERROR, "Error: FIFO underrun (advance)"); 112 | } else { 113 | f->tail += n; f->tail %= f->size; 114 | f->fill -= n; 115 | ret = 1; 116 | } 117 | 118 | MUTEX_UNLOCK(&f->mutex); 119 | 120 | return ret; 121 | } 122 | 123 | int fifo_read(Fifo *f, unsigned char *byte, unsigned short n) 124 | { 125 | int i = 0; 126 | if (n == 0) return 0; 127 | 128 | MUTEX_LOCK(&f->mutex); 129 | 130 | if (f->fill == 0) { 131 | f->unr = HIGH; 132 | netpp_log(DCLOG_ERROR, "Error: FIFO underrun (read)"); 133 | i = 0; 134 | } else { 135 | do { 136 | *byte++ = f->buf[f->tail++]; 137 | f->tail %= f->size; 138 | n--; i++; 139 | } while (f->tail != f->head && n > 0); 140 | f->fill -= i; 141 | } 142 | 143 | MUTEX_UNLOCK(&f->mutex); 144 | 145 | return i; 146 | } 147 | 148 | int fifo_fill(Fifo *f) 149 | { 150 | unsigned short fill; 151 | MUTEX_LOCK(&f->mutex); 152 | fill = f->fill; 153 | MUTEX_UNLOCK(&f->mutex); 154 | return fill; 155 | } 156 | 157 | int fifo_status(Fifo *f, char which, int width, char *flags) 158 | { 159 | MUTEX_LOCK(&f->mutex); 160 | 161 | if (which == FIFO_WRITE) { 162 | if (f->fill >= f->size - width) { 163 | flags[TXF] = HIGH; flags[TXAF] = LOW; 164 | if (f->fill == f->size) flags[TXF] = LOW; 165 | } else { 166 | flags[TXF] = HIGH; flags[TXAF] = HIGH; 167 | } 168 | } else { 169 | if (f->fill <= width) { 170 | flags[RXE] = HIGH; flags[RXAE] = LOW; 171 | if (f->fill == 0) flags[RXE] = LOW; 172 | } else { 173 | flags[RXE] = HIGH; flags[RXAE] = HIGH; 174 | } 175 | } 176 | MUTEX_UNLOCK(&f->mutex); 177 | return 1; 178 | } 179 | 180 | void fifo_reset(Fifo *f) 181 | { 182 | f->unr = LOW; 183 | f->ovr = LOW; 184 | } 185 | 186 | int fifo_write(Fifo *f, const unsigned char *byte, unsigned short n) 187 | { 188 | int i = 0; 189 | 190 | if (n == 0) return 0; 191 | 192 | MUTEX_LOCK(&f->mutex); 193 | 194 | if (f->fill == f->size) { 195 | f->ovr = HIGH; 196 | netpp_log(DCLOG_ERROR, "Error: FIFO overrun"); 197 | i = 0; 198 | } else { 199 | do { 200 | f->buf[f->head++] = *byte++; 201 | f->head %= f->size; 202 | n--; i++; 203 | } while (f->tail != f->head && n > 0); 204 | 205 | f->fill += i; 206 | } 207 | 208 | MUTEX_UNLOCK(&f->mutex); 209 | 210 | return i; 211 | } 212 | 213 | /** This is the FIFO filler/emptier 214 | * 215 | * ..for the FALLTHROUGH FIFO type. 216 | * 217 | * \param flag 0: read (set out), 1: write (get in) 218 | * 219 | * 220 | */ 221 | 222 | void fifo_pump(struct duplexfifo_t *df, struct fat_pointer *data, char *flag) 223 | { 224 | // printf("in: %p, out: %p, flag: %x\n", in, out, flag); 225 | static 226 | unsigned char valuebytes[32]; 227 | unsigned char rx, tx; 228 | short nbits = data->bounds->len; 229 | short nbytes = (nbits + 7) >> 3; 230 | int error; 231 | 232 | Fifo *fifo_in, *fifo_out; 233 | 234 | fifo_in = &df->in; fifo_out = &df->out; 235 | 236 | // Guard maximum chunk size: 237 | if (nbytes > sizeof(valuebytes)) { 238 | nbytes = sizeof(valuebytes); 239 | netpp_log(DCLOG_ERROR, "Warning: FIFO request size truncated"); 240 | } 241 | 242 | 243 | // Buffer action flags: 244 | rx = flag[RXE] == HIGH ? 1 : 0; 245 | tx = flag[TXF] == HIGH ? 1 : 0; 246 | 247 | // Check W1C error flags: 248 | if (flag[OVR] == HIGH) fifo_out->ovr = LOW; 249 | if (flag[UNR] == HIGH) fifo_in->unr = LOW; 250 | 251 | // Do we write? 252 | if (tx) { 253 | error = fifo_in->fromlogic(data->base, nbytes, valuebytes); 254 | if (error < 0) { 255 | netpp_log(DCLOG_ERROR, "%s: Bad FIFO value @%d", __FILE__, fifo_in->fill); 256 | } 257 | fifo_write(fifo_in, valuebytes, nbytes); 258 | } 259 | 260 | // Did we read advance? 261 | if (rx) { 262 | // printf("S <- H fill: %d\n", fifo_out->fill); 263 | fifo_advance(fifo_out, nbytes); 264 | } 265 | 266 | // Query status and set flags 267 | fifo_status(fifo_in, FIFO_WRITE, nbytes, flag); 268 | fifo_status(fifo_out, FIFO_READ, nbytes, flag); 269 | 270 | if (flag[RXE] == HIGH) { // We do at least have 'nbytes' bytes in the FIFO 271 | fifo_get(fifo_out, valuebytes, nbytes); 272 | fifo_out->tologic(data->base, nbytes, valuebytes); 273 | // printf("n: %d: %02x %02x\n", n, valuebytes[0], valuebytes[1]); 274 | } else { 275 | fill_slv(data->base, nbits, UNDEFINED); 276 | } 277 | 278 | // Return OVR/UNR flags 279 | flag[OVR] = fifo_in->ovr; 280 | flag[UNR] = fifo_out->unr; 281 | } 282 | 283 | void sim_fifo_rxtx(duplexfifo_t_ghdl *fifo, 284 | struct fat_pointer *data, char *flag) 285 | { 286 | DuplexFifo *f = (DuplexFifo *) fifo[0]; 287 | fifo_pump(f, data, flag); 288 | } 289 | 290 | 291 | #ifdef SUPPORT_LEGACY_FIFO 292 | void sim_fifo_io(struct fat_pointer *data, char *flag) 293 | { 294 | fifo_pump(&g_dfifo, data, flag); 295 | } 296 | #endif 297 | 298 | duplexfifo_t_ghdl sim_fifo_new_wrapped(string_ghdl name, integer_ghdl size, 299 | integer_ghdl wordsize) 300 | { 301 | char propname[64]; 302 | int error; 303 | struct duplexfifo_t *df = 304 | (struct duplexfifo_t *) malloc(sizeof(struct duplexfifo_t)); 305 | ghdlname_to_propname(name->base, propname, sizeof(propname)); 306 | netpp_log(DCLOG_VERBOSE, "Reserved FIFO '%s' with word size %d, size 0x%x", propname, 307 | wordsize, size * sizeof(uint16_t)); 308 | 309 | error = fifo_init(&df->in, size, wordsize); 310 | if (error < 0) return NULL; 311 | error = fifo_init(&df->out, size, wordsize); 312 | if (error < 0) return NULL; 313 | 314 | error = register_fifo(df, propname); 315 | if (error < 0) return 0; 316 | return (duplexfifo_t_ghdl) df; 317 | } 318 | 319 | void_ghdl sim_fifo_del(duplexfifo_t_ghdl *fifo) 320 | { 321 | DuplexFifo *f = fifo[0]; 322 | fifo_exit(&f->out); 323 | fifo_exit(&f->in); 324 | } 325 | 326 | //////////////////////////////////////////////////////////////////////////// 327 | // Auxiliary, blocking reads 328 | 329 | int fifo_blocking_read(Fifo *f, unsigned char *buf, unsigned int n) 330 | { 331 | int i; 332 | int retry = g_fifoconfig.retry; 333 | 334 | while (n > 0) { 335 | while (!fifo_fill(f)) { 336 | USLEEP(g_fifoconfig.timeout); 337 | //netpp_log(DCLOG_ERROR, 338 | //"%s(): FIFO retry #%d (requested: %d)\n", __FUNCTION__, retry, n); 339 | retry--; 340 | if (retry == 0) return DCERR_COMM_TIMEOUT; 341 | } 342 | i = fifo_read(f, buf, n); 343 | buf += i; n -= i; 344 | // printf("Read %d from FIFO (%d left)\n", i, n); 345 | } 346 | return n; 347 | } 348 | 349 | int fifo_blocking_write(Fifo *f, unsigned char *buf, unsigned int n) 350 | { 351 | int i; 352 | int retry = g_fifoconfig.retry; 353 | 354 | while (n) { 355 | while (fifo_fill(f) == f->size ) { 356 | USLEEP(g_fifoconfig.timeout); 357 | retry--; 358 | if (retry == 0) return DCERR_COMM_TIMEOUT; 359 | } 360 | 361 | i = fifo_write(f, buf, n); 362 | // printf("Wrote %d to FIFO\n", i); 363 | buf += i; n -= i; 364 | } 365 | return n; 366 | } 367 | 368 | // not first fall through 369 | // Unused and unmaintained. 370 | #if 0 371 | void sim_fifo_io_ex(char *data, char *flag) 372 | { 373 | uint32_t v; 374 | unsigned char b; 375 | 376 | 377 | // Check W1C error flags: 378 | if (flag[OVR] == HIGH) g_fifos[TO_SIM].unr = LOW; 379 | if (flag[UNR] == HIGH) g_fifos[FROM_SIM].ovr = LOW; 380 | 381 | // Are we serious to operate? 382 | if (flag[RXE] == HIGH) { 383 | if (fifo_read(&g_fifos[TO_SIM], &b, 1) == 1) { 384 | uint_to_logic(data, 8, b); 385 | } 386 | } 387 | 388 | if (flag[TXF] == HIGH) { 389 | logic_to_uint(data, 8, &v); 390 | b = v; 391 | fifo_write(&g_fifos[FROM_SIM], &b, 1); 392 | } 393 | 394 | // Query status and set flags 395 | fifo_status(&g_fifos[TO_SIM], FIFO_READ, flag); 396 | fifo_status(&g_fifos[FROM_SIM], FIFO_WRITE, flag); 397 | 398 | // Return OVR/UNR flags 399 | flag[OVR] = g_fifos[FROM_SIM].ovr; 400 | flag[UNR] = g_fifos[TO_SIM].unr; 401 | } 402 | 403 | #endif 404 | -------------------------------------------------------------------------------- /src/fifo.h: -------------------------------------------------------------------------------- 1 | /** \file fifo.h 2 | * 3 | * Software FIFO internal functions 4 | * 5 | * (c) 2009-2011 Martin Strubel 6 | * 7 | * Modified for ghdl extension. 8 | * See LICENSE.txt in this distribution for usage terms. 9 | * 10 | */ 11 | 12 | #include "threadaux.h" 13 | 14 | /** \defgroup FIFO Internal software FIFO 15 | * 16 | * This module implements a software FIFO in first fall through mode 17 | * usable by several concurrent threads. 18 | * 19 | * Its behaviour is close to the typical FIFO chips like 20 | * Cypress FX2, FT2232 variants, etc. 21 | * 22 | */ 23 | 24 | /** \addtogroup FIFO 25 | * \{ 26 | */ 27 | 28 | #define RXE 0 ///< RX empty, low active 29 | #define TXF 1 ///< TX full, low active 30 | #define RXAE 2 ///< RX data in buffer 31 | #define TXAF 3 ///< TX data can be written 32 | #define OVR 4 ///< Overrun bit, high active 33 | #define UNR 5 ///< Underrun bit, high active 34 | 35 | #define FIFO_READ 0 ///< Select FIFO_READ queue 36 | #define FIFO_WRITE 1 ///< Select FIFO_WRITE queue 37 | 38 | /* The software FIFO descriptor structure */ 39 | struct fifo_t { 40 | unsigned char *buf; 41 | unsigned short size; 42 | unsigned short head; 43 | unsigned short tail; 44 | unsigned short fill; 45 | unsigned char ovr; // Overrun (wrote when full) 46 | unsigned char unr; // Underrun (read when empty) 47 | MUTEX mutex; // FIFO locking for concurrent threads 48 | void (*tologic)(char *l, int n, const void *b); 49 | int (*fromlogic)(char *l, int n, void *b); 50 | }; 51 | 52 | struct duplexfifo_t { 53 | struct fifo_t in; 54 | struct fifo_t out; 55 | }; 56 | 57 | /** Fifo structure, anonymous */ 58 | typedef struct fifo_t Fifo; 59 | 60 | /** DuplexFifo structure, anonymous */ 61 | typedef struct duplexfifo_t DuplexFifo; 62 | 63 | /** Initialize a FIFO 64 | * \param size Size of the FIFO in data elements (not necessarily bytes) 65 | * \param wordsize 1: Bytes, 2: 16 bit words 66 | * 67 | * */ 68 | 69 | int fifo_init(Fifo *f, unsigned short size, unsigned short wordsize); 70 | 71 | /** Release FIFO resources */ 72 | 73 | void fifo_exit(Fifo *f); 74 | 75 | /** Read from FIFO 76 | * \param buf Buffer to copy to from FIFO 77 | * \param n Number of bytes to read 78 | * 79 | * \return Number of bytes read 80 | * 81 | */ 82 | 83 | int fifo_read(Fifo *f, unsigned char *buf, unsigned short n); 84 | 85 | /** Write to FIFO 86 | * \param buf Buffer to copy from to FIFO 87 | * \param n Number of bytes to read 88 | * 89 | * \return Number of bytes read 90 | * 91 | */ 92 | 93 | int fifo_write(Fifo *f, const unsigned char *buf, unsigned short n); 94 | 95 | /** Returns FIFO status depending on the 'which' argument: 96 | * #FIFO_WRITE: Check if bytes can be written 97 | * #FIFO_READ: Check if bytes available from FIFO 98 | * 99 | * \param which Either #FIFO_WRITE or #FIFO_READ, see above 100 | * \param width Data output width of FIFO (normally 1 for 8 bit, 2 for 16 bit) 101 | * \param flags Pointer to array of flags changed by this function 102 | * 103 | * \return 1: FIFO ready, 0: Full/empty 104 | */ 105 | 106 | int fifo_status(Fifo *f, char which, int width, char *flags); 107 | 108 | /** Return FIFO fill state (number of bytes) */ 109 | int fifo_fill(Fifo *f); 110 | 111 | /** \} */ 112 | -------------------------------------------------------------------------------- /src/framebuf.c: -------------------------------------------------------------------------------- 1 | /** Virtual Framebuffer implementation via netpp display server 2 | * 3 | * (c) 2011, Martin Strubel 4 | * 5 | */ 6 | #include 7 | #include 8 | #include "netpp.h" 9 | #include "display/display.h" 10 | #include "ghpi.h" 11 | #include "netppwrap.h" 12 | #include 13 | 14 | #define MAX_NUM_FBS 1 15 | 16 | 17 | typedef struct { 18 | unsigned long size; 19 | unsigned short type; 20 | unsigned short width, height; 21 | void *data; 22 | unsigned short bpp; 23 | DEVICE device; 24 | TOKEN token; 25 | } FBuffer; 26 | 27 | static 28 | FBuffer *s_fbs[MAX_NUM_FBS] = { 0 }; 29 | 30 | void sim_releasefb(FBHANDLE handle); 31 | 32 | FBHANDLE new_fb(void) 33 | { 34 | int i; 35 | FBuffer *fb; 36 | 37 | for (i = 0; i < MAX_NUM_FBS; i++) { 38 | if (s_fbs[i] == 0) { 39 | fb = (FBuffer *) malloc(sizeof(FBuffer)); 40 | if (!fb) return -1; 41 | s_fbs[i] = fb; 42 | return i; 43 | } 44 | } 45 | return -1; 46 | } 47 | 48 | void del_fb(FBHANDLE handle) 49 | { 50 | free(s_fbs[handle]); 51 | s_fbs[handle] = 0; 52 | } 53 | 54 | void break_handler(int n) 55 | { 56 | int i; 57 | int val = 1; 58 | printf("Hit Ctrl-C, cleaning up...\n"); 59 | 60 | for (i = 0; i < MAX_NUM_FBS; i++) { 61 | if (GET_FB(i)) { 62 | printf("Terminating stream %d\n", i); 63 | set_property(GET_FB(i)->device, "Stream.Stop", &val, DC_COMMAND); 64 | sim_releasefb(i); 65 | } 66 | } 67 | 68 | exit(-1); 69 | } 70 | 71 | 72 | framebuffer_t_ghdl sim_initfb(DEVHANDLE dev, 73 | integer_ghdl w, integer_ghdl h, integer_ghdl type) 74 | { 75 | unsigned long size; 76 | void *buf; 77 | short bpp = 16; 78 | short pixelsize; 79 | int error; 80 | int i; 81 | 82 | FBHANDLE handle = new_fb(); 83 | if (handle < 0) return handle; 84 | 85 | FBuffer *fb = GET_FB(handle); 86 | 87 | DEVICE d = get_device(dev); 88 | 89 | switch (type) { 90 | case VIDEOMODE_8BIT: bpp = 8; 91 | case VIDEOMODE_INDEXED: break; 92 | case VIDEOMODE_UYVY: break; 93 | default: 94 | return -1; 95 | } 96 | 97 | pixelsize = (bpp + 7) / 8; 98 | size = w * h * pixelsize; 99 | buf = malloc(size); 100 | if (!buf) return -1; 101 | memset(buf, 0, size); 102 | fb->size = size; 103 | fb->data = buf; 104 | fb->width = w; 105 | fb->height = h; 106 | fb->type = type; 107 | fb->bpp = bpp; 108 | fb->device = d; 109 | 110 | error = dcProperty_ParseName(d, "Stream.Data", &fb->token); 111 | if (error < 0) { 112 | fprintf(stderr, 113 | "Doesn't seem to be a netpp display server we're talking to\n"); 114 | return error; 115 | } 116 | 117 | // Configure display: 118 | set_property(d, "Stream.X", &w, DC_INT); 119 | set_property(d, "Stream.Y", &h, DC_INT); 120 | i = bpp; set_property(d, "Stream.BitsPerPixel", &i, DC_MODE); 121 | set_property(d, "Mode", &type, DC_MODE); 122 | i = 1; set_property(d, "Stream.Start", &i, DC_COMMAND); 123 | 124 | signal(SIGINT, break_handler); 125 | 126 | return 0; 127 | } 128 | 129 | void sim_setpixel(FBHANDLE handle, int x, int y, char *slv) 130 | { 131 | int error; 132 | unsigned long offset; 133 | unsigned short bits, bytes; 134 | uint32_t val; 135 | FBuffer *fb = GET_FB(handle); 136 | 137 | bits = fb->bpp; 138 | bytes = (bits + 7) / 8; 139 | 140 | offset = y * fb->width + x; 141 | 142 | error = logic_to_uint(slv, bits, &val); 143 | #ifdef DEBUG 144 | if (error < 0) { 145 | printf("Undefined pixel value at (%d, %d)\n", x, y); 146 | val = 0xffffffff; 147 | } 148 | #endif 149 | 150 | switch (bytes) { 151 | case 2: 152 | ( (uint16_t *) fb->data )[offset] = val; 153 | break; 154 | case 1: 155 | ( (uint8_t *) fb->data )[offset] = val; 156 | break; 157 | } 158 | } 159 | 160 | void sim_releasefb(FBHANDLE handle) 161 | { 162 | FBuffer *fb = GET_FB(handle); 163 | free(fb->data); 164 | del_fb(handle); 165 | } 166 | 167 | void sim_updatefb(FBHANDLE handle) 168 | { 169 | FBuffer *fb = GET_FB(handle); 170 | printf("Updating buffer size %ld\n", fb->size); 171 | set_buffer(fb->device, fb->token, fb->data, fb->size); 172 | } 173 | 174 | 175 | void sim_setfb(FBHANDLE handle, struct ghdl_string *pixdata) 176 | { 177 | FBuffer *fb = GET_FB(handle); 178 | unsigned long size; 179 | short pixelsize; 180 | uint32_t val; 181 | uint16_t *dst2; 182 | uint8_t *dst1; 183 | short bpp; 184 | 185 | size = pixdata->bounds->len; 186 | 187 | if (size > fb->size) { 188 | size = fb->size; 189 | fprintf(stderr, "Warning: Size truncated\n"); 190 | } 191 | 192 | char *data = pixdata->base; 193 | 194 | bpp = fb->bpp; 195 | pixelsize = (bpp + 7) / 8; 196 | 197 | switch (pixelsize) { 198 | case 2: 199 | dst2 = (uint16_t *) fb->data; 200 | while (size--) { 201 | logic_to_uint(data, bpp, &val); 202 | data += bpp; 203 | *dst2++ = val; 204 | } 205 | break; 206 | case 1: 207 | dst1 = (uint8_t *) fb->data; 208 | while (size--) { 209 | logic_to_uint(data, bpp, &val); 210 | data += bpp; 211 | *dst1++ = val; 212 | } 213 | break; 214 | default: return; 215 | } 216 | } 217 | 218 | 219 | -------------------------------------------------------------------------------- /src/handler.c: -------------------------------------------------------------------------------- 1 | /** Handler example code. 2 | * 3 | * All handlers (getters and setters) start with get_ respectively with 4 | * set_. 5 | * 6 | * Note that for readonly/writeonly properties, only the relevant handler 7 | * functions need to be specified. 8 | * 9 | */ 10 | 11 | #include // printf debugging only 12 | #include 13 | #include "devlib.h" 14 | #include "devlib_error.h" 15 | #include "registermap.h" 16 | #include "property_protocol.h" 17 | #include "fifo.h" 18 | 19 | #include "vpi_user.h" 20 | #include "ghpi.h" 21 | 22 | // #define DEBUG 23 | 24 | int fifo_blocking_read(Fifo *f, unsigned char *buf, unsigned int n); 25 | int fifo_blocking_write(Fifo *f, unsigned char *buf, unsigned int n); 26 | 27 | // Global variables exposed to property access: 28 | 29 | struct fifoconfig g_fifoconfig = { 30 | .timeout = 100000, // Default FIFO timeout 31 | .retry = 30 32 | }; 33 | 34 | struct vpi_handle_cache { 35 | vpiHandle emuir; 36 | }; 37 | 38 | // Global netpp accessible bus: 39 | Bus *g_bus = 0; 40 | 41 | 42 | /* 43 | int get_uint32(DEVICE d, DCValue *out) 44 | { 45 | vpiHandle v; 46 | s_spi_value vpival; 47 | 48 | uint32_t u32; 49 | 50 | v = vpi_put_value(vpi_handle_cache.emuir, &vpival); 51 | if (format != vpiVectorVal) return DCERR_PROPERTY_TYPE_MATCH; 52 | 53 | 54 | } 55 | */ 56 | 57 | int get_fifo(Fifo *f, DCValue *out) 58 | { 59 | int warn = 0; 60 | int n; 61 | 62 | static unsigned char buf[BUFSIZE]; 63 | 64 | switch (out->type) { 65 | case DC_COMMAND: // This is a buffer update action 66 | // netpp_log(DCLOG_VERBOSE, "Release buffer"); 67 | break; 68 | case DC_UNDEFINED: 69 | case DC_BUFFER: 70 | // You must do a buffer size check here: 71 | // netpp_log(DCLOG_VERBOSE, "Get buffer, len %d", out->len); 72 | if (out->len > BUFSIZE) { 73 | out->len = BUFSIZE; 74 | warn = DCWARN_PROPERTY_MODIFIED; 75 | } 76 | 77 | if (out->len == 0) { // Python handler 78 | n = fifo_fill(f); 79 | out->len = n; 80 | // We must return this to Python for proper buffer 81 | // reservation 82 | return DCERR_PROPERTY_SIZE_MATCH; 83 | } else { 84 | #ifdef DEBUG 85 | printf("----------------------------------------\n"); 86 | printf("H <- S fill: %d\n", fifo_fill(f)); 87 | printf("Request %ld bytes\n", out->len); 88 | #endif 89 | n = fifo_blocking_read(f, buf, out->len); 90 | if (n < 0) { 91 | printf("FIFO timed out\n"); 92 | return DCERR_COMM_TIMEOUT; 93 | } 94 | // Set data gathering pointer: 95 | } 96 | out->value.p = buf; // ONLY BECAUSE IT'S STATIC!! 97 | break; 98 | default: 99 | return DCERR_PROPERTY_TYPE_MATCH; 100 | } 101 | return warn; 102 | } 103 | 104 | int set_fifo(Fifo *f, DCValue *in) 105 | { 106 | int error; 107 | int warn = 0; 108 | 109 | static unsigned char buf[BUFSIZE]; 110 | 111 | switch (in->type) { 112 | case DC_COMMAND: // This is a buffer update action 113 | // Fill in update code 114 | // netpp_log(DCLOG_VERBOSE, "Update buffer len %d", in->len); 115 | error = fifo_blocking_write(f, buf, in->len); 116 | if (error < 0) return error; 117 | break; 118 | case DC_UNDEFINED: 119 | case DC_BUFFER: 120 | // You must do a buffer size check here: 121 | // netpp_log(DCLOG_VERBOSE, "Set buffer len %d", in->len); 122 | if (in->len > BUFSIZE) { 123 | in->len = BUFSIZE; 124 | return DCERR_PROPERTY_SIZE_MATCH; 125 | } 126 | 127 | // Tell engine where the data will go to: 128 | in->value.p = buf; // ONLY BECAUSE IT'S STATIC! 129 | break; 130 | default: 131 | return DCERR_PROPERTY_TYPE_MATCH; 132 | } 133 | 134 | return warn; 135 | } 136 | 137 | /* Custom FIFO handler for netpp */ 138 | 139 | int handle_fifo(void *p, int write, DCValue *val) 140 | { 141 | // printf("%s (%d)\n", __FUNCTION__, write); 142 | DuplexFifo *df = (DuplexFifo *) p; 143 | if (write) { 144 | return set_fifo(&df->out, val); 145 | } else { 146 | return get_fifo(&df->in, val); 147 | } 148 | } 149 | 150 | int handle_fifo_infill(DuplexFifo *df, int write, DCValue *out) 151 | { 152 | if (write) return DCERR_PROPERTY_ACCESS; 153 | out->value.i = fifo_fill(&df->in); 154 | return 0; 155 | } 156 | 157 | int handle_fifo_outfill(DuplexFifo *df, int write, DCValue *out) 158 | { 159 | if (write) return DCERR_PROPERTY_ACCESS; 160 | out->value.i = fifo_fill(&df->out); 161 | return 0; 162 | } 163 | 164 | int handle_pty(void *pty, int write, DCValue *out) 165 | { 166 | return DCERR_PROPERTY_HANDLER; 167 | } 168 | 169 | 170 | /** Dummy register space. Just a RAM. 171 | * This is accessed by sim_regmap_read()/sim_regmap_write() 172 | */ 173 | 174 | #ifdef SUPPORT_LEGACY_REGISTERMAP 175 | 176 | static unsigned char _registermap[256] = { 177 | 0xaa, 0x55, 178 | }; 179 | 180 | // FIXME: No more global stuff 181 | 182 | MUTEX reg_mutex; 183 | 184 | void init_registermap(void) 185 | { 186 | MUTEX_INIT(®_mutex); 187 | // Default Throttle on: 188 | _registermap[R_FPGA_Registers_Control] = THROTTLE; 189 | } 190 | 191 | #endif 192 | 193 | int device_write(RemoteDevice *d, 194 | uint32_t addr, const unsigned char *buf, 195 | unsigned long size) 196 | { 197 | #ifdef SUPPORT_LEGACY_REGISTERMAP 198 | 199 | if (addr < VBUS_ADDR_OFFSET) { 200 | printf("Write to register %04x:", addr); 201 | 202 | MUTEX_LOCK(®_mutex); 203 | memcpy(&_registermap[addr & 0xff], buf, size); 204 | MUTEX_UNLOCK(®_mutex); 205 | 206 | } else { 207 | #endif 208 | #ifdef DEBUG 209 | printf("Write to VBUS %04x (%lu bytes)\n", addr, size); 210 | hexdump(buf, size); 211 | #endif 212 | 213 | if (!g_bus) return DCERR_BADPTR; 214 | g_bus->addr = addr; 215 | return bus_write(g_bus, buf, size); 216 | 217 | #ifdef SUPPORT_LEGACY_REGISTERMAP 218 | } 219 | #endif 220 | return 0; 221 | } 222 | 223 | /** Device flat address register map read access. 224 | * For low level device access (SPI, I2C, etc.) this normally wants to 225 | * be implemented 226 | * 227 | * NOTE: We define register sizes in BITS! Therefore, convert to bytes 228 | * for host side processing. 229 | */ 230 | 231 | int device_read(RemoteDevice *d, 232 | uint32_t addr, unsigned char *buf, unsigned long size) 233 | { 234 | #ifdef SUPPORT_LEGACY_REGISTERMAP 235 | if (addr < VBUS_ADDR_OFFSET) { 236 | printf("Read from register %04x (%lu bytes)\n", addr, size); 237 | MUTEX_LOCK(®_mutex); 238 | memcpy(buf, &_registermap[addr & 0xff], size); 239 | MUTEX_UNLOCK(®_mutex); 240 | } else { 241 | #endif 242 | #ifdef DEBUG 243 | printf("Read from VBUS %04x (%lu bytes)\n", addr, size); 244 | #endif 245 | // Make sure no write is still pending: 246 | if (!g_bus) return DCERR_BADPTR; 247 | g_bus->addr = addr; 248 | return bus_read(g_bus, buf, size); 249 | 250 | #ifdef SUPPORT_LEGACY_REGISTERMAP 251 | } 252 | #endif 253 | return 0; 254 | } 255 | 256 | #ifdef SUPPORT_LEGACY_REGISTERMAP 257 | void sim_regmap_read(regaddr_t_ghdl address, unsigned_ghdl data) 258 | { 259 | int nbytes; 260 | uint32_t addr, val; 261 | logic_to_uint(address, sizeof(regaddr_t_ghdl), &addr); 262 | addr &= 0xff; 263 | 264 | nbytes = (data->bounds->len + 7) >> 3; 265 | 266 | val = 0; 267 | // Big endian shift: 268 | MUTEX_LOCK(®_mutex); 269 | while (nbytes--) { 270 | val <<= 8; 271 | val |= _registermap[addr++]; 272 | } 273 | MUTEX_UNLOCK(®_mutex); 274 | uint_to_logic(data->base, data->bounds->len, val); 275 | } 276 | 277 | void sim_regmap_write(regaddr_t_ghdl address, unsigned_ghdl data) 278 | { 279 | uint32_t addr, val; 280 | int nbytes; 281 | logic_to_uint(address, sizeof(regaddr_t_ghdl), &addr); 282 | addr &= 0xff; 283 | logic_to_uint(data->base, data->bounds->len, &val); 284 | 285 | nbytes = (data->bounds->len + 7) >> 3; 286 | addr += nbytes - 1; 287 | MUTEX_LOCK(®_mutex); 288 | while (nbytes--) { 289 | _registermap[addr--] = val & 0xff; 290 | val >>= 8; 291 | } 292 | MUTEX_UNLOCK(®_mutex); 293 | } 294 | #endif 295 | 296 | int handle_vbus_width(Bus *b, int write, DCValue *val) 297 | { 298 | val->value.i = b->width; 299 | return 0; 300 | } 301 | 302 | int handle_vbus_addr(Bus *b, int write, DCValue *val) 303 | { 304 | if (write) { 305 | b->addr = (uint32_t) val->value.i; 306 | } else { 307 | val->value.i = b->addr; 308 | } 309 | return 0; 310 | } 311 | 312 | int handle_vbus_data(Bus *b, int write, DCValue *val) 313 | { 314 | int error = 0; 315 | switch (val->type) { 316 | case DC_BUFFER: 317 | case DC_STRING: 318 | val->value.p = b->tmpbuf; 319 | if (val->len > b->bufsize) { 320 | val->len = b->bufsize; 321 | error = DCWARN_PROPERTY_MODIFIED; 322 | // Python "dump" query support: 323 | } else 324 | // Are we reading? Then fire a request. 325 | if (val->len == 0) { 326 | val->len = 16; // Packet of 16 bytes is default 327 | error = DCERR_PROPERTY_SIZE_MATCH; 328 | } 329 | 330 | if (error >= 0 && !write) { 331 | error = bus_read(b, b->tmpbuf, val->len); 332 | } 333 | break; 334 | case DC_UNDEFINED: 335 | break; 336 | case DC_COMMAND: 337 | if (write) { 338 | error = bus_write(b, b->tmpbuf, val->len); 339 | } else error = 0; 340 | break; 341 | case DC_REGISTER: 342 | if (write) { 343 | error = bus_val_wr(b, b->addr, val->value.i); 344 | } else { 345 | error = bus_val_rd(b, b->addr, (uint32_t *) &val->value.i); 346 | } 347 | error = 0; 348 | break; 349 | default: 350 | error = DCERR_PROPERTY_TYPE_MATCH; 351 | } 352 | return error; 353 | } 354 | 355 | -------------------------------------------------------------------------------- /src/helpers.c: -------------------------------------------------------------------------------- 1 | /** \file helpers.c 2 | * 3 | * GHDL simulator interface auxiliaries 4 | * 5 | * (c) 2009-2011 Martin Strubel 6 | * 7 | */ 8 | 9 | #include 10 | #ifdef __WIN32__ 11 | #include // abuse htons/ntohs 12 | #endif 13 | 14 | #ifdef __linux__ 15 | #include 16 | #endif 17 | #include "ghpi.h" 18 | 19 | /** Dump buffer */ 20 | 21 | void hexdump(const char *buf, unsigned long n) 22 | { 23 | int i = 0; 24 | int c = 0; 25 | 26 | while (i < n) { 27 | // Testing: Bitreverse display: 28 | // printf("%02x ", reverse32(buf[2 * i]) >> 24); 29 | printf("%02x ", (unsigned char) buf[i]); 30 | c++; 31 | if (c == 16) { c = 0; printf("\r\n"); } 32 | i++; 33 | } 34 | if (c) 35 | printf("\r\n"); 36 | } 37 | 38 | char slv_desc(unsigned char c) 39 | { 40 | char *s = "UX01Z???"; 41 | 42 | c &= 7; 43 | 44 | return s[c]; 45 | } 46 | 47 | int logic_to_uint(const char *l, int nbits, uint32_t *val) 48 | { 49 | uint32_t v = 0; 50 | int error = 0; 51 | while (nbits--) { 52 | v <<= 1; 53 | switch (*l) { 54 | case HIGH: v |= 1; break; 55 | case LOW: break; 56 | default: 57 | fprintf(stderr, "Warning: Undefined value('%c')[%d] in %s\n", 58 | slv_desc(*l), nbits, __FUNCTION__); 59 | *val = 0xffffffff; 60 | error = -1; 61 | } 62 | l++; 63 | } 64 | *val = v; 65 | return error; 66 | } 67 | 68 | void uint_to_logic(char *l, int nbits, uint32_t val) 69 | { 70 | uint32_t pos; 71 | 72 | while (nbits--) { 73 | pos = 1 << nbits; 74 | if (val & pos) { 75 | *l = HIGH; 76 | } else { 77 | *l = LOW; 78 | } 79 | l++; 80 | } 81 | } 82 | 83 | int logic_to_bytes(char *l, int n, void *data) 84 | { 85 | uint32_t v; 86 | uint8_t *b = (uint8_t *) data; 87 | int err; 88 | 89 | while (n--) { 90 | err = logic_to_uint(l, 8, &v); 91 | if (err < 0) { 92 | return err; 93 | } 94 | *b++ = v; l += 8; 95 | } 96 | return err; 97 | } 98 | 99 | int logic_to_words(char *l, int n, void *data) 100 | { 101 | uint16_t *w = (uint16_t *) data; 102 | uint32_t v; 103 | int err; 104 | 105 | while (n--) { 106 | err = logic_to_uint(l, 16, &v); 107 | if (err < 0) { 108 | return err; 109 | } 110 | *w++ = htons(v); l += 16; 111 | } 112 | 113 | return err; 114 | } 115 | 116 | void bytes_to_logic(char *l, int n, const void *data) 117 | { 118 | uint8_t *b = (uint8_t *) data; 119 | while (n--) { 120 | uint_to_logic(l, 8, *b++); 121 | l += 8; 122 | } 123 | } 124 | 125 | void words_to_logic(char *l, int n, const void *data) 126 | { 127 | const uint16_t *w = (const uint16_t *) data; 128 | 129 | while (n--) { 130 | uint_to_logic(l, 16, ntohs(*w++)); 131 | l += 16; 132 | } 133 | } 134 | 135 | void fill_slv(char *l, int nbits, unsigned char val) 136 | { 137 | while (nbits--) { 138 | *l++ = val; 139 | } 140 | } 141 | 142 | // TESTING 143 | 144 | void sim_set_ptr(handle_t_ghdl p) 145 | { 146 | printf("Got ptr: %p\n", p); 147 | } 148 | 149 | handle_t_ghdl sim_get_ptr(netpphandle_t_ghdl i) 150 | { 151 | printf("Got int: %x\n", i); 152 | return (void *) 0xdeadbeef; 153 | } 154 | 155 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | /** \file 2 | * 3 | * Main program launcher to start GHDL simulation from 4 | * 5 | */ 6 | #include 7 | #include 8 | #include "ghpi.h" 9 | 10 | int ghdl_main(int argc, char **argv); 11 | 12 | int main(int argc, char **argv) 13 | { 14 | int error; 15 | const char *name; 16 | 17 | #ifdef CONFIG_NETPP_EARLY_INIT 18 | name = basename(argv[0]); 19 | 20 | error = netpp_root_init(name); 21 | #endif 22 | if (error >= 0) { 23 | error = ghdl_main(argc, argv); 24 | } 25 | return error; 26 | } 27 | -------------------------------------------------------------------------------- /src/netppwrap.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * \brief netpp wrapper functions 3 | * 4 | * Functions to register a virtual entity with the netpp VPI handler. 5 | * 6 | * (c) 2012, 7 | * 8 | */ 9 | 10 | /** \defgroup VPIwrapper Dynamic entity exporting 11 | * 12 | * \example vpiwrapper.c 13 | */ 14 | 15 | /** \addtogroup VPIwrapper 16 | * \{ */ 17 | 18 | int netpp_is_initialized(void); 19 | 20 | /** Netpp explicit master initialization. Call early in your HDL. 21 | * 22 | */ 23 | int netpp_master_init(const char *name); 24 | 25 | /** Netpp root node initialization. Call before registering any 26 | * properties. 27 | * 28 | * \param name Name of root node (device name, really) 29 | */ 30 | int netpp_root_init(const char *name); 31 | 32 | /** Creates a property name from a VHDL string 33 | * \param name The VHDL string pointing to a name 34 | * \param propname Buffer to a string 35 | * \param len Length of above buffer for size check 36 | * 37 | * \warning This function may change the behaviour, i.e. the property 38 | * name translation. 39 | */ 40 | int ghdlname_to_propname(const char *name, char *propname, int len); 41 | 42 | /** Register a Virtual RAM entity. 43 | * A shared netpp RAM can be read out and manipulated a RAM while emulating 44 | * a standard dual port RAM on the VHDL side. 45 | * \param entity Pointer to a Ram descriptor structure 46 | * \param name The unique property name for the entity 47 | */ 48 | int register_ram(void *entity, char *name); 49 | 50 | /** Register Virtual Bus entity 51 | * \param entity Pointer to a Virtual Bus structure 52 | * \param name The unique property name for the entity 53 | */ 54 | int register_bus(void *entity, char *name); 55 | 56 | /** Register a Virtual FIFO entity. 57 | * \param entity Pointer to a FIFO descriptor structure 58 | * \param name The unique property name for the entity 59 | */ 60 | int register_fifo(void *entity, char *name); 61 | 62 | /** \} */ 63 | #define FBHANDLE uint32_t 64 | #define DEVHANDLE uint32_t 65 | 66 | #define GET_FB(x) s_fbs[x] 67 | 68 | struct _propertydesc; 69 | 70 | DEVICE get_device(DEVHANDLE dev); 71 | int set_property(DEVICE d, const char *name, void *val, int type); 72 | 73 | int set_buffer(DEVICE d, TOKEN t, void *buf, int len); 74 | 75 | struct _propertydesc *property_desc_new(const struct _propertydesc *template); 76 | struct _propertydesc *property_string_new(int size); 77 | -------------------------------------------------------------------------------- /src/pipe.c: -------------------------------------------------------------------------------- 1 | /** \file pipe.c 2 | * 3 | * Simple named pipe reader/writer for GHDL simulation interface 4 | * 5 | * (c) 2011 Martin Strubel 6 | * 7 | */ 8 | 9 | #include "ghpi.h" 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define RXE 0 ///< RX empty, low active 19 | #define TXF 1 ///< TX full, low active 20 | #define OUR 2 ///< Over/Underrun bit 21 | #define ERR 3 ///< Generic error bit 22 | 23 | #define TIMEOUT 0 24 | 25 | int sim_openpipe(struct ghdl_string *name) 26 | { 27 | int l; 28 | char fname[32]; 29 | 30 | l = name->bounds->len; 31 | if (l >= 32) return -ENOMEM; 32 | strncpy(fname, name->base, l); 33 | fname[l] = '\0'; 34 | return open(fname, O_RDWR | O_NONBLOCK); 35 | } 36 | 37 | void sim_closepipe(int fd) 38 | { 39 | close(fd); 40 | } 41 | 42 | void sim_pipe_rxtx(int fd, char *data, char *flag) 43 | { 44 | unsigned char buf[2]; 45 | int stat; 46 | struct pollfd fds; 47 | fds.fd = fd; 48 | fds.events = POLLIN | POLLOUT | POLLERR; 49 | 50 | if (flag[TXF] == HIGH) { // we issue a write 51 | logic_to_bytes(data, 1, buf); 52 | stat = write(fd, buf, 1); 53 | if (stat != 1) flag[OUR] = HIGH; 54 | } 55 | 56 | if (flag[RXE] == HIGH) { // we issue a read 57 | stat = read(fd, buf, 1); 58 | if (stat == 1) { 59 | bytes_to_logic(data, 1, buf); 60 | } else { 61 | flag[OUR] = HIGH; 62 | } 63 | } 64 | 65 | flag[RXE] = LOW; 66 | flag[TXF] = LOW; 67 | stat = poll(&fds, 1, TIMEOUT); 68 | if (stat < 0) flag[ERR] = HIGH; // Mark error 69 | else { 70 | if (fds.revents & POLLIN) flag[RXE] = HIGH; 71 | if (fds.revents & POLLOUT) flag[TXF] = HIGH; 72 | } 73 | } 74 | 75 | void sim_pipe_in(int fd, char *data, char *flag) 76 | { 77 | unsigned char buf[2]; 78 | int stat; 79 | struct pollfd fds; 80 | fds.fd = fd; 81 | fds.events = POLLIN | POLLERR; 82 | 83 | if (flag[RXE] == HIGH) { // we issue a read 84 | stat = read(fd, buf, 1); 85 | if (stat == 1) { 86 | bytes_to_logic(data, 1, buf); 87 | } else { 88 | flag[OUR] = HIGH; 89 | } 90 | } 91 | stat = poll(&fds, 1, TIMEOUT); 92 | if (stat < 0) flag[ERR] = HIGH; // Mark error 93 | else if (stat > 0) { 94 | flag[RXE] = HIGH; 95 | } else { 96 | flag[RXE] = LOW; 97 | } 98 | } 99 | 100 | void sim_pipe_out(int fd, char *data, char *flag) 101 | { 102 | unsigned char buf[2]; 103 | int stat; 104 | struct pollfd fds; 105 | fds.fd = fd; 106 | fds.events = POLLOUT | POLLERR; 107 | 108 | if (flag[TXF] == HIGH) { // we issue a write 109 | logic_to_bytes(data, 1, buf); 110 | stat = write(fd, buf, 1); 111 | if (stat != 1) flag[OUR] = HIGH; 112 | } 113 | stat = poll(&fds, 1, TIMEOUT); 114 | if (stat < 0) flag[ERR] = HIGH; // Mark error 115 | else if (stat > 0) { 116 | flag[TXF] = HIGH; 117 | } else { 118 | flag[TXF] = LOW; 119 | } 120 | } 121 | 122 | -------------------------------------------------------------------------------- /src/project.mk: -------------------------------------------------------------------------------- 1 | CSRCS = helpers.c 2 | 3 | ifdef DEBUG 4 | CFLAGS += -DDEBUG -g 5 | endif 6 | 7 | # Important flag for external static compilation, we don't want to 8 | # use import library symbols... 9 | CFLAGS-$(CONFIG_MINGW32) += -DMSVC_STATIC 10 | CFLAGS-$(CONFIG_NETPP) += -I$(NETPP)/include -I$(NETPP)/devices 11 | CFLAGS-$(CONFIG_NETPP) += -DUSE_NETPP 12 | CFLAGS-$(CONFIG_LINUX) += -fPIC 13 | CFLAGS-$(CONFIG_LEGACY) += -DSUPPORT_LEGACY_FIFO 14 | 15 | CFLAGS += $(CFLAGS-y) 16 | 17 | CSRCS-$(CONFIG_NETPP) += netpp.c ram.c fifo.c bus.c 18 | CSRCS-$(CONFIG_NETPP) += handler.c 19 | CSRCS-$(CONFIG_NETPP_DISPLAY) += framebuf.c 20 | 21 | CSRCS-$(CONFIG_MINGW32) += threadaux.c 22 | CSRCS-$(CONFIG_LINUX) += pipe.c 23 | CSRCS-$(CONFIG_MINGW32) += winpipe.c 24 | 25 | CSRCS += $(CSRCS-y) 26 | 27 | -------------------------------------------------------------------------------- /src/propbuild.c: -------------------------------------------------------------------------------- 1 | /** Dynamic property table building from iteration capable structure */ 2 | 3 | #include "property_types.h" 4 | #include "dynprops.h" 5 | 6 | typedef struct { 7 | int (*descent)(void **node); 8 | int (*next)(void **node); 9 | int (*property_from_node)(void *node, DynPropertyDesc *p); 10 | unsigned char *buffer; 11 | unsigned int len; 12 | unsigned int size; 13 | } Iterator; 14 | 15 | int build_proplist(Iterator *it, void **node) 16 | { 17 | DynPropertyDesc *p; 18 | void *n = *node; 19 | 20 | if (n == 0) return 0; 21 | 22 | if (it->len + sizeof(*p) >= it->size) { 23 | printf("Out of memory\n"); 24 | return ERR_MALLOC; 25 | } 26 | 27 | ret = it->property_from_node(n, &buffer[len]); 28 | if (ret < 0) return ret; 29 | 30 | 31 | 32 | } 33 | 34 | #if TEST 35 | 36 | set_rootprop(rootname, p); 37 | 38 | 39 | int build_props(char *rootname, int n, DynPropertyDesc **props); 40 | { 41 | DynPropertyDesc *p; 42 | int n; 43 | int i; 44 | 45 | 46 | 47 | 48 | 49 | 50 | *props = p; 51 | return 0; 52 | } 53 | 54 | int main(int argc, char **argv) 55 | { 56 | 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/ram.c: -------------------------------------------------------------------------------- 1 | /** Virtual RAM implementation v2 2 | * 3 | * 2015, Martin Strubel 4 | * 5 | * Note 1: We assume that client and server are running on the same 6 | * endianness (typically little). This will turn out in a mess when 7 | * porting ghdlex to other endian architectures. 8 | * For now, we live with it. 9 | * 10 | * Note 2: netpp handles endianness, however, the raw FIFO code does 11 | * not. Transferred buffers are always byte-oriented! 12 | * 13 | * Note 3: This RAM, due to configureable address widths, has 14 | * BIG ENDIAN conversion routines. Otherwise, the I/O conversion will 15 | * follow the hosts endianness, which caused a mess in the v1 implementation. 16 | * 17 | */ 18 | 19 | #include 20 | #include 21 | #include "ghpi.h" 22 | #include "netpp.h" 23 | #include "netppwrap.h" 24 | #include "property_protocol.h" // netpp_log 25 | 26 | typedef struct RamDesc { 27 | unsigned short addrsize; 28 | uint32_t offset; 29 | short bitwidth; 30 | short width; 31 | int size; 32 | } Ram; 33 | 34 | static 35 | void endian_safe_memory_copy(unsigned char *dest, uint32_t v, int sz) 36 | { 37 | int s = sz * 8; 38 | while (sz--) { 39 | s -= 8; 40 | *dest++ = (v >> s) & 0xff; 41 | } 42 | } 43 | 44 | static 45 | void endian_safe_value_copy(uint32_t *v, unsigned char *dest, int sz) 46 | { 47 | uint32_t val = 0; 48 | while (sz--) { 49 | val <<= 8; 50 | val |= *dest++; 51 | } 52 | *v = val; 53 | } 54 | 55 | rambuf_t_ghdl sim_ram_new_wrapped(string_ghdl name, integer_ghdl bits, 56 | integer_ghdl size) 57 | { 58 | char propname[64]; 59 | int error; 60 | int n = 1 << size; 61 | int ws = (bits + 7) / 8; 62 | if (bits > 32) { 63 | netpp_log(DCLOG_ERROR, "More than 32 bits not supported. Abort."); 64 | return NULL; 65 | } 66 | Ram *r = (Ram *) calloc(1, n * ws + sizeof(Ram)); 67 | r->addrsize = size; 68 | r->bitwidth = bits; 69 | r->width = ws; 70 | r->offset = 0; 71 | r->size = n; 72 | ghdlname_to_propname(name->base, propname, sizeof(propname)); 73 | netpp_log(DCLOG_NOTICE, "Reserved RAM '%s' with word size 0x%x(%d bytes), width: %d bits", 74 | propname, 75 | r->size, r->size * ws, bits); 76 | 77 | error = register_ram(r, propname); 78 | if (error < 0) return 0; 79 | return (rambuf_t_ghdl) r; 80 | } 81 | 82 | void_ghdl sim_ram_write(rambuf_t_ghdl *ram, 83 | struct fat_pointer *addr, ram_port_t_ghdl data) 84 | { 85 | unsigned char *p; 86 | uint32_t i, val; 87 | Ram *r = (Ram *) ram[0]; 88 | 89 | p = (unsigned char *) &r[1]; 90 | logic_to_uint(data, 8 * sizeof(val), &val); 91 | logic_to_uint(addr->base, r->addrsize, &i); 92 | if (i > r->size) { 93 | netpp_log(DCLOG_ERROR, "write: Bad boundaries; addr = %08x", i); 94 | return; 95 | } 96 | #ifdef ENABLE_RAM_TRACE 97 | printf("Write %08x : %08x\n", i, val); 98 | #endif 99 | endian_safe_memory_copy(&p[i * r->width], val, r->width); 100 | } 101 | 102 | void_ghdl sim_ram_read(rambuf_t_ghdl *ram, 103 | struct fat_pointer *addr, ram_port_t_ghdl data) 104 | { 105 | int error; 106 | unsigned char *p; 107 | uint32_t i, val; 108 | Ram *r = (Ram *) ram[0]; 109 | p = (unsigned char *) &r[1]; 110 | error = logic_to_uint(addr->base, r->addrsize, &i); 111 | if (error < 0) { 112 | netpp_log(DCLOG_ERROR, "RAM content undefined"); 113 | } 114 | if (i > r->size) { 115 | netpp_log(DCLOG_ERROR, "read: Bad boundaries; addr = %08x", i); 116 | return; 117 | } 118 | endian_safe_value_copy(&val, &p[i * r->width], r->width); 119 | uint_to_logic(data, 8 * sizeof(val), val); 120 | } 121 | 122 | void_ghdl sim_ram_del(rambuf_t_ghdl *ram) 123 | { 124 | free(ram[0]); 125 | } 126 | 127 | //////////////////////////////////////////////////////////////////////////// 128 | // netpp handlers to read out RAM remotely 129 | 130 | int set_ram(DEVICE d, DCValue *in) 131 | { 132 | Ram *r = (Ram *) d; 133 | if (!r) return DCERR_BADPTR; 134 | int size = r->size * r->width; 135 | 136 | size -= r->offset; 137 | 138 | switch (in->type) { 139 | case DC_COMMAND: // This is a buffer update action 140 | break; 141 | case DC_INVALID: 142 | case DC_BUFFER: 143 | // You must do a buffer size check here: 144 | // netpp_log(DCLOG_VERBOSE, "Set buffer len %d", in->len); 145 | if (in->len > size) { 146 | in->len = size; 147 | return DCERR_PROPERTY_SIZE_MATCH; 148 | } 149 | 150 | // Tell engine where the data will go to: 151 | in->value.p = &((char *) &r[1])[r->offset]; 152 | break; 153 | default: 154 | return DCERR_PROPERTY_TYPE_MATCH; 155 | } 156 | return 0; 157 | } 158 | 159 | int get_ram(DEVICE d, DCValue *out) 160 | { 161 | int ret = 0; 162 | Ram *r = (Ram *) d; 163 | if (!r) return DCERR_BADPTR; 164 | 165 | int size = r->size * r->width; 166 | size -= r->offset; 167 | 168 | switch (out->type) { 169 | case DC_COMMAND: // This is a buffer update action 170 | break; 171 | case DC_UNDEFINED: 172 | case DC_BUFFER: 173 | // You must do a buffer size check here: 174 | if (out->len > size) { 175 | out->len = size; 176 | ret = DCWARN_PROPERTY_MODIFIED; 177 | } else 178 | if (out->len == 0) { // Python handler 179 | out->len = size; 180 | // We must return this to Python for proper buffer 181 | // reservation 182 | return DCERR_PROPERTY_SIZE_MATCH; 183 | } 184 | 185 | // Tell engine where the data will come from: 186 | out->value.p = &((char *) &r[1])[r->offset]; 187 | 188 | break; 189 | default: 190 | ret = DCERR_PROPERTY_TYPE_MATCH; 191 | } 192 | 193 | return ret; 194 | } 195 | 196 | /** Netpp custom handler */ 197 | int handle_rambuf(void *p, int write, DCValue *val) 198 | { 199 | // printf("%s (%d)\n", __FUNCTION__, write); 200 | if (write) { 201 | return set_ram((DEVICE) p, val); 202 | } else { 203 | return get_ram((DEVICE) p, val); 204 | } 205 | } 206 | 207 | int handle_ramoffset(void *p, int write, DCValue *val) 208 | { 209 | Ram *r = (Ram *) p; 210 | if (write) { 211 | if (val->value.u >= (r->size * r->width)) 212 | return DCERR_PROPERTY_RANGE; 213 | r->offset = val->value.i; 214 | } 215 | else val->value.u = r->offset; 216 | return 0; 217 | } 218 | -------------------------------------------------------------------------------- /src/threadaux.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "threadaux.h" 3 | 4 | int my_mutex_init(MUTEX *mutex) 5 | { 6 | *mutex = CreateMutex(NULL, FALSE, NULL); 7 | if (!*mutex) return -1; 8 | return 0; 9 | } 10 | 11 | int my_mutex_exit(MUTEX *mutex) 12 | { 13 | CloseHandle(*mutex); 14 | return 0; 15 | } 16 | 17 | int my_mutex_lock(MUTEX *mutex) 18 | { 19 | int error; 20 | 21 | int tries = 100; 22 | 23 | do { 24 | tries--; 25 | error = WaitForSingleObject(*mutex, 100); 26 | } while (error != WAIT_OBJECT_0 && tries); 27 | 28 | if (error != WAIT_OBJECT_0) return -1; 29 | 30 | return 0; 31 | } 32 | 33 | int my_mutex_unlock(MUTEX *mutex) 34 | { 35 | if (!ReleaseMutex(*mutex)) return -1; 36 | return 0; 37 | } 38 | 39 | -------------------------------------------------------------------------------- /src/threadaux.h: -------------------------------------------------------------------------------- 1 | /** \file 2 | * 3 | * Thread auxiliaries (platform dependent) 4 | */ 5 | 6 | #ifdef __WIN32__ 7 | #include 8 | // #include "property_protocol.h" 9 | #define MUTEX HANDLE 10 | // int mutex_init(MUTEX *m, void *p); 11 | // int mutex_lock(MUTEX *m); 12 | // int mutex_unlock(MUTEX *m); 13 | #define USLEEP(x) Sleep(x / 1000) 14 | #define MUTEX_LOCK my_mutex_lock 15 | #define MUTEX_UNLOCK my_mutex_unlock 16 | #define MUTEX_INIT my_mutex_init 17 | #define MUTEX_EXIT my_mutex_exit 18 | 19 | int my_mutex_init(MUTEX *mutex); 20 | int my_mutex_exit(MUTEX *mutex); 21 | int my_mutex_lock(MUTEX *mutex); 22 | int my_mutex_unlock(MUTEX *mutex); 23 | 24 | 25 | #else 26 | #include 27 | #define USLEEP usleep 28 | #define MUTEX pthread_mutex_t 29 | #define MUTEX_INIT(m) pthread_mutex_init(m, NULL) 30 | #define MUTEX_LOCK pthread_mutex_lock 31 | #define MUTEX_UNLOCK pthread_mutex_unlock 32 | #define MUTEX_EXIT pthread_mutex_destroy 33 | #endif 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/vpi_user.h: -------------------------------------------------------------------------------- 1 | #ifndef __vpi_user_H 2 | #define __vpi_user_H 3 | /* 4 | * Copyright (c) 1999 Stephen Williams (steve@icarus.com) 5 | * 6 | * This source code is free software; you can redistribute it 7 | * and/or modify it in source code form under the terms of the GNU 8 | * General Public License as published by the Free Software 9 | * Foundation; either version 2 of the License, or (at your option) 10 | * any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program; if not, write to the Free Software 19 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 20 | */ 21 | #ifdef HAVE_CVS_IDENT 22 | #ident "$Id: vpi_user.h,v 1.2 2002/12/30 15:31:54 ballance Exp $" 23 | #endif 24 | 25 | 26 | #if defined(__MINGW32__) || defined (__CYGWIN32__) 27 | # define DLLEXPORT __declspec(dllexport) 28 | #else 29 | # define DLLEXPORT 30 | #endif 31 | 32 | #ifdef __cplusplus 33 | # define EXTERN_C_START extern "C" { 34 | # define EXTERN_C_END } 35 | #else 36 | # define EXTERN_C_START 37 | # define EXTERN_C_END 38 | #endif 39 | 40 | #ifndef __GNUC__ 41 | # undef __attribute__ 42 | # define __attribute__(x) 43 | #endif 44 | 45 | EXTERN_C_START 46 | 47 | # include 48 | 49 | typedef struct __vpiHandle *vpiHandle; 50 | 51 | /* 52 | * This structure is created by the VPI application to provide hooks 53 | * into the application that the compiler/simulator can access. 54 | */ 55 | typedef struct t_vpi_systf_data { 56 | int type; 57 | int subtype; 58 | char *tfname; 59 | int (*calltf)(char*); 60 | int (*compiletf)(char*); 61 | int (*sizetf)(); 62 | char *user_data; 63 | } s_vpi_systf_data, *p_vpi_systf_data; 64 | 65 | /* The type in the above structure can have one of the following 66 | values: */ 67 | #define vpiSysTask 1 68 | #define vpiSysFunc 2 69 | 70 | typedef struct t_vpi_vlog_info 71 | { 72 | int argc; 73 | char **argv; 74 | char *product; 75 | char *version; 76 | } s_vpi_vlog_info, *p_vpi_vlog_info; 77 | 78 | 79 | typedef struct t_vpi_time { 80 | int type; 81 | unsigned int high; 82 | unsigned int low; 83 | double real; 84 | } s_vpi_time, *p_vpi_time; 85 | 86 | #define vpiScaledRealTime 1 87 | #define vpiSimTime 2 88 | #define vpiSuppressTime 3 89 | 90 | typedef struct t_vpi_vecval { 91 | int aval, bval; /* ab encoding: 00=0, 10=1, 11=X, 01=Z */ 92 | } s_vpi_vecval, *p_vpi_vecval; 93 | 94 | typedef struct t_vpi_strengthval { 95 | int logic; 96 | int s0, s1; 97 | } s_vpi_strengthval, *p_vpi_strengthval; 98 | 99 | /* 100 | * This structure holds values that are passed back and forth between 101 | * the simulator and the application. 102 | */ 103 | typedef struct t_vpi_value { 104 | int format; 105 | union { 106 | char *str; 107 | int scalar; 108 | int integer; 109 | double real; 110 | struct t_vpi_time *time; 111 | struct t_vpi_vecval *vector; 112 | struct t_vpi_strengthval *strength; 113 | char *misc; 114 | } value; 115 | } s_vpi_value, *p_vpi_value; 116 | 117 | /* These are valid codes for the format of the t_vpi_value structure. */ 118 | #define vpiBinStrVal 1 119 | #define vpiOctStrVal 2 120 | #define vpiDecStrVal 3 121 | #define vpiHexStrVal 4 122 | #define vpiScalarVal 5 123 | #define vpiIntVal 6 124 | #define vpiRealVal 7 125 | #define vpiStringVal 8 126 | #define vpiVectorVal 9 127 | #define vpiStrengthVal 10 128 | #define vpiTimeVal 11 129 | #define vpiObjTypeVal 12 130 | #define vpiSuppressVal 13 131 | 132 | #define vpiScalar 17 /* scalar (boolean) */ 133 | #define vpiVector 18 /* vector (boolean) */ 134 | 135 | /* SCALAR VALUES */ 136 | #define vpi0 0 137 | #define vpi1 1 138 | #define vpiZ 2 139 | #define vpiX 3 140 | #define vpiH 4 141 | #define vpiL 5 142 | #define vpiDontCare 6 143 | 144 | /* STRENGTH VALUES */ 145 | #define vpiSupplyDrive 0x80 146 | #define vpiStrongDrive 0x40 147 | #define vpiPullDrive 0x20 148 | #define vpiLargeCharge 0x10 149 | #define vpiWeakDrive 0x08 150 | #define vpiMediumCharge 0x04 151 | #define vpiSmallCharge 0x02 152 | #define vpiHiZ 0x01 153 | 154 | /* OBJECT CODES */ 155 | #define vpiConstant 7 156 | #define vpiFunction 20 157 | #define vpiIntegerVar 25 158 | #define vpiIterator 27 159 | #define vpiMemory 29 160 | #define vpiMemoryWord 30 161 | #define vpiModule 32 162 | #define vpiNamedBegin 33 163 | #define vpiNamedEvent 34 164 | #define vpiNamedFork 35 165 | #define vpiNet 36 166 | #define vpiReg 48 167 | #define vpiSysFuncCall 56 168 | #define vpiSysTaskCall 57 169 | #define vpiTask 59 170 | #define vpiTimeVar 63 171 | #define vpiIndex 78 172 | #define vpiLeftRange 79 173 | #define vpiRightRange 83 174 | #define vpiScope 84 175 | #define vpiSysTfCall 85 176 | #define vpiArgument 89 177 | #define vpiInternalScope 92 178 | #define vpiVariables 100 179 | 180 | #define vpiCallback 1000 181 | 182 | /* PROPERTIES */ 183 | #define vpiType 1 184 | #define vpiName 2 185 | #define vpiFullName 3 186 | #define vpiSize 4 187 | #define vpiTimeUnit 11 188 | #define vpiTimePrecision 12 189 | #define vpiConstType 43 190 | # define vpiDecConst 1 191 | # define vpiRealConst 2 192 | # define vpiBinaryConst 3 193 | # define vpiOctConst 4 194 | # define vpiHexConst 5 195 | # define vpiStringConst 6 196 | #define vpiSigned 65 197 | /* IVL private properties */ 198 | #define _vpiNexusId 128 199 | 200 | /* DELAY MODES */ 201 | #define vpiNoDelay 1 202 | #define vpiInertialDelay 2 203 | #define vpiTransportDelay 3 204 | #define vpiPureTransportDelay 4 205 | 206 | #define vpiForceFlag 5 207 | #define vpiReleaseFlag 6 208 | 209 | 210 | /* VPI FUNCTIONS */ 211 | extern void vpi_register_systf(const struct t_vpi_systf_data*ss); 212 | extern void vpi_printf(const char*fmt, ...) 213 | __attribute__((format (printf,1,2))); 214 | 215 | /* vpi_vprintf is non-standard. */ 216 | extern void vpi_vprintf(const char*fmt, va_list ap); 217 | 218 | extern unsigned int vpi_mcd_close(unsigned int mcd); 219 | extern char *vpi_mcd_name(unsigned int mcd); 220 | extern unsigned int vpi_mcd_open(char *name); 221 | extern unsigned int vpi_mcd_open_x(char *name, char *mode); 222 | extern int vpi_mcd_printf(unsigned int mcd, const char*fmt, ...) 223 | __attribute__((format (printf,2,3))); 224 | extern int vpi_mcd_fputc(unsigned int mcd, unsigned char x); 225 | extern int vpi_mcd_fgetc(unsigned int mcd); 226 | 227 | /* 228 | * support for VPI callback functions. 229 | */ 230 | typedef struct t_cb_data { 231 | int reason; 232 | int (*cb_rtn)(struct t_cb_data*cb); 233 | vpiHandle obj; 234 | p_vpi_time time; 235 | p_vpi_value value; 236 | int index; 237 | char*user_data; 238 | } s_cb_data, *p_cb_data; 239 | 240 | #define cbValueChange 1 241 | #define cbStmt 2 242 | #define cbForce 3 243 | #define cbRelease 4 244 | #define cbAtStartOfSimTime 5 245 | #define cbReadWriteSynch 6 246 | #define cbReadOnlySynch 7 247 | #define cbNextSimTime 8 248 | #define cbAfterDelay 9 249 | #define cbEndOfCompile 10 250 | #define cbStartOfSimulation 11 251 | #define cbEndOfSimulation 12 252 | #define cbError 13 253 | #define cbTchkViolation 14 254 | #define cbStartOfSave 15 255 | #define cbEndOfSave 16 256 | #define cbStartOfRestart 17 257 | #define cbEndOfRestart 18 258 | #define cbStartOfReset 19 259 | #define cbEndOfReset 20 260 | #define cbEnterInteractive 21 261 | #define cbExitInteractive 22 262 | #define cbInteractiveScopeChange 23 263 | #define cbUnresolvedSystf 24 264 | 265 | extern vpiHandle vpi_register_cb(p_cb_data data); 266 | extern int vpi_remove_cb(vpiHandle ref); 267 | 268 | /* 269 | * This function allows a vpi application to control the simulation 270 | * engine. The operation parameter specifies the function to 271 | * perform. The remaining parameters (if any) are interpreted by the 272 | * operation. The vpi_sim_control definition was added to P1364-2000 273 | * 14 July 1999. See PLI Task Force ID: PTF-161 274 | * 275 | * vpiFinish - perform the $finish operation, as soon as the user 276 | * function returns. This operation takes a single 277 | * parameter, a diagnostic exit code. 278 | * 279 | * vpiStop - 280 | * vpiReset - 281 | * vpiSetInteractiveScope - 282 | */ 283 | extern void vpi_control(int operation, ...); 284 | #define vpiStop 1 285 | #define vpiFinish 2 286 | #define vpiReset 3 287 | #define vpiSetInteractiveScope 4 288 | 289 | /* vpi_sim_control is the incorrect name for vpi_control. */ 290 | extern void vpi_sim_control(int operation, ...); 291 | 292 | extern vpiHandle vpi_handle(int type, vpiHandle ref); 293 | extern vpiHandle vpi_iterate(int type, vpiHandle ref); 294 | extern vpiHandle vpi_scan(vpiHandle iter); 295 | extern vpiHandle vpi_handle_by_index(vpiHandle ref, int index); 296 | extern vpiHandle vpi_handle_by_name(char*name, vpiHandle scope); 297 | 298 | extern void vpi_get_time(vpiHandle obj, s_vpi_time*t); 299 | extern int vpi_get(int property, vpiHandle ref); 300 | extern char* vpi_get_str(int property, vpiHandle ref); 301 | extern void vpi_get_value(vpiHandle expr, p_vpi_value value); 302 | 303 | /* 304 | * This function puts a value into the object referenced by the 305 | * handle. This assumes that the value supports having its value 306 | * written to. The time parameter specifies when the assignment is to 307 | * take place. This allows you to schedule an assignment to happen in 308 | * the future. 309 | * 310 | * The flags value specifies the delay model to use in assigning the 311 | * value. This specifies how the time value is to be used. 312 | * 313 | * vpiNoDelay -- Set the value immediately. The p_vpi_time parameter 314 | * may be NULL, in this case. This is like a blocking assignment 315 | * in behavioral code. 316 | * 317 | * vpiInertialDelay -- Set the value using the transport delay. The 318 | * p_vpi_time parameter is required and specifies when the 319 | * assignment is to take place. This is like a non-blocking 320 | * assignment in behavioral code. 321 | */ 322 | extern vpiHandle vpi_put_value(vpiHandle obj, p_vpi_value value, 323 | p_vpi_time when, int flags); 324 | 325 | extern int vpi_free_object(vpiHandle ref); 326 | extern int vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p); 327 | 328 | 329 | /* 330 | * Support for handling errors. 331 | */ 332 | typedef struct t_vpi_error_info { 333 | int state; 334 | int level; 335 | char*message; 336 | char*product; 337 | char*code; 338 | char*file; 339 | int line; 340 | } s_vpi_error_info, *p_vpi_error_info; 341 | 342 | /* error_info states */ 343 | # define vpiCompile 1 344 | # define vpiPLI 2 345 | # define vpiRun 3 346 | 347 | /* error_info levels */ 348 | # define vpiNotice 1 349 | # define vpiWarning 2 350 | # define vpiError 3 351 | # define vpiSystem 4 352 | # define vpiInternal 5 353 | 354 | extern int vpi_chk_error(p_vpi_error_info info); 355 | 356 | 357 | /* This is the table of startup routines included in each module. */ 358 | extern DLLEXPORT void (*vlog_startup_routines[])(); 359 | 360 | EXTERN_C_END 361 | 362 | /* 363 | * $Log: vpi_user.h,v $ 364 | * Revision 1.2 2002/12/30 15:31:54 ballance 365 | * Check-in of removal of tix elements. Now, just WaveWidget is left... 366 | * 367 | * Revision 1.15 2002/08/12 01:35:01 steve 368 | * conditional ident string using autoconfig. 369 | * 370 | * Revision 1.14 2002/07/19 01:57:26 steve 371 | * Add vpi_chk_error and vpi_control functions. 372 | * 373 | * Revision 1.13 2002/07/17 05:13:43 steve 374 | * Implementation of vpi_handle_by_name, and 375 | * add the vpiVariables iterator. 376 | * 377 | * Revision 1.12 2002/06/21 04:59:35 steve 378 | * Carry integerness throughout the compilation. 379 | * 380 | * Revision 1.11 2002/05/24 19:05:30 steve 381 | * support GCC __attributes__ for printf formats. 382 | * 383 | * Revision 1.10 2002/05/23 03:34:46 steve 384 | * Export the vpi_vprintf function. 385 | * 386 | * Revision 1.9 2002/05/18 02:34:11 steve 387 | * Add vpi support for named events. 388 | * 389 | * Add vpi_mode_flag to track the mode of the 390 | * vpi engine. This is for error checking. 391 | * 392 | * Revision 1.8 2002/05/17 16:13:08 steve 393 | * Add vpiIndex update. 394 | * 395 | * Revision 1.7 2002/01/24 04:19:39 steve 396 | * Add the vpiLeft.. and vpiRightRange constants 397 | * 398 | * Revision 1.6 2001/09/30 05:18:46 steve 399 | * Reduce VCD output by removing duplicates. (Stephan Boettcher) 400 | * 401 | * Revision 1.5 2001/05/20 15:09:40 steve 402 | * Mingw32 support (Venkat Iyer) 403 | * 404 | * Revision 1.4 2001/05/10 00:16:00 steve 405 | * Add the vpi_user strength definitions. 406 | * 407 | * Revision 1.3 2001/04/25 04:45:52 steve 408 | * Implement vpi_put_value for signals. 409 | * 410 | * Revision 1.2 2001/03/22 02:23:17 steve 411 | * fgetc patch from Peter Monta. 412 | * 413 | * Revision 1.1 2001/03/19 01:21:45 steve 414 | * vpi_user header file is a root header. 415 | */ 416 | #endif 417 | -------------------------------------------------------------------------------- /src/vpiwrapper.c: -------------------------------------------------------------------------------- 1 | /** \brief VPI example for access of structures via netpp 2 | * 3 | * 2012, hackfin@section5.ch 4 | * 5 | * This VPI wraps all the signals it finds on the toplevel into 6 | * dynamic netpp properties that can be queried and manipulated via 7 | * the various netpp tools. 8 | * 9 | * Important little nasty detail and feature: 10 | * This can only be used with a netpp version >= 0.4 which supports 11 | * (and is configured accordingly) dynamic properties. 12 | * 13 | * Moreover, you must have the simulator executable configured such that 14 | * it uses both libslave and libmysim as shared library (.so). 15 | * libslave.so must not have any weak symbols included for these stubs: 16 | * 17 | * - device_read() 18 | * - device_write() 19 | * - local_getroot() 20 | * 21 | * This is necessary for the dynamic libraries to determine the proper root 22 | * node of the simulator netpp device by only calling these functions 23 | * from libmysim. 24 | * 25 | * This is potentially funky behaviour is due to the following reasons: 26 | * - Some modules may not call any netpp functionality: netpp.vpi allows 27 | * access of the exported (entity) signals 28 | * - Some modules may already register specific properties with netpp: 29 | * These are registered "on top" of the properties that netpp.vpi exports 30 | * from the signals 31 | * - Some modules use specific VPIs with default static properties. 32 | * That means, they have a set of default properties plus some dynamic 33 | * properties that are depending on its configuration. 34 | * 35 | * All these modules should also be able to be called without a specific 36 | * VPI attached. 37 | * 38 | * Intermixing dynamic and static properties is possible from netpp 0.4 39 | * with the existing mechanism of class derivation. A VPI registers a 40 | * default static property table and adds a dynamic root node providing 41 | * a reference to the static properties (base class). 42 | * 43 | */ 44 | 45 | #include 46 | #include 47 | #include 48 | #include "devlib_types.h" 49 | #include "devlib_error.h" 50 | #include "property_types.h" 51 | #include "slave.h" 52 | #include "dynprops.h" 53 | #include "vpi_user.h" 54 | #include "netppwrap.h" 55 | 56 | int binstr_to_uint(const char *l, int nbits, uint32_t *val) 57 | { 58 | uint32_t v = 0; 59 | while (nbits--) { 60 | v <<= 1; 61 | switch (*l) { 62 | case '1': v |= 1; break; 63 | case '0': break; 64 | default: 65 | fprintf(stderr, "Undefined value in %s\n", __FUNCTION__); 66 | *val = 0xffffffff; 67 | return -1; 68 | } 69 | l++; 70 | } 71 | *val = v; 72 | return 0; 73 | } 74 | 75 | void uint_to_binstr(char *l, int nbits, uint32_t val) 76 | { 77 | uint32_t pos; 78 | 79 | while (nbits--) { 80 | pos = 1 << nbits; 81 | if (val & pos) { 82 | *l = '1'; 83 | } else { 84 | *l = '0'; 85 | } 86 | l++; 87 | } 88 | } 89 | 90 | int v_handler(void *p, int write, DCValue *val) 91 | { 92 | s_vpi_value v; 93 | char str[32]; 94 | int size; 95 | 96 | static 97 | s_vpi_time time = { 98 | .type = vpiSimTime, 99 | .high = 0, 100 | .low = 1200, 101 | }; 102 | 103 | uint32_t uval; 104 | 105 | v.format = vpiBinStrVal; 106 | 107 | // Obtain size: 108 | vpi_get_value((vpiHandle) p, &v); 109 | size = strlen(v.value.str); 110 | 111 | 112 | if (write) { 113 | switch (val->type) { 114 | case DC_COMMAND: 115 | uint_to_binstr(str, size, val->value.i); 116 | v.value.str = str; 117 | vpi_put_value((vpiHandle) p, &v, &time, vpiInertialDelay); 118 | return 0; 119 | case DC_BOOL: 120 | if (val->value.i) { 121 | str[0] = '1'; 122 | } else { 123 | str[0] = '0'; 124 | } 125 | break; 126 | case DC_REGISTER: 127 | case DC_MODE: 128 | case DC_INT: 129 | uint_to_binstr(str, size, val->value.i); 130 | break; 131 | case DC_BUFFER: 132 | // TODO 133 | return DCERR_PROPERTY_TYPE_MATCH; 134 | default: 135 | return DCERR_PROPERTY_TYPE_MATCH; 136 | } 137 | v.value.str = str; 138 | vpi_put_value((vpiHandle) p, &v, &time, vpiInertialDelay); 139 | // vpi_put_value((vpiHandle) p, &v, NULL, vpiNoDelay); 140 | } else { 141 | if (binstr_to_uint(v.value.str, size, &uval) < 1) { 142 | fprintf(stderr, "Conversion warning in %s\n", __FUNCTION__); 143 | } 144 | val->value.i = uval; 145 | } 146 | return 0; 147 | } 148 | 149 | PropertyDesc s_property_template = { 150 | .type = DC_BOOL, 151 | .flags = F_RW, 152 | .where = DC_CUSTOM, 153 | .access = { .custom = { v_handler, 0 } }, 154 | }; 155 | 156 | TOKEN property_from_signal(TOKEN parent, vpiHandle sig) 157 | { 158 | const char *name; 159 | int size; 160 | PropertyDesc *p; 161 | TOKEN t; 162 | s_vpi_value v; 163 | 164 | name = vpi_get_str(vpiName, sig); 165 | v.format = vpiBinStrVal; 166 | vpi_get_value(sig, &v); 167 | if (!v.value.str) { 168 | return TOKEN_INVALID; 169 | } 170 | size = strlen(v.value.str); 171 | 172 | p = property_desc_new(&s_property_template); 173 | if (!p) return TOKEN_INVALID; 174 | 175 | // Store vpi handle in property 176 | p->access.custom.p = sig; 177 | 178 | if (size <= 32) { 179 | if (size == 1) 180 | p->type = DC_BOOL; 181 | else 182 | p->type = DC_REGISTER; 183 | } else { 184 | p->type = DC_BUFFER; 185 | } 186 | 187 | t = new_dynprop(name, p); 188 | dynprop_append(parent, t); 189 | return t; 190 | } 191 | 192 | static vpiHandle s_vpictrl = 0; 193 | 194 | int scan(struct t_cb_data *cb) 195 | { 196 | const char *name; 197 | vpiHandle top_iter; 198 | vpiHandle module; 199 | vpiHandle sig_iter; 200 | vpiHandle scope; 201 | vpiHandle sig; 202 | 203 | TOKEN root; 204 | TOKEN t; 205 | 206 | root = local_getroot(NULL); 207 | 208 | top_iter = vpi_iterate(vpiModule, NULL); 209 | module = vpi_scan(top_iter); 210 | if (module == NULL) { 211 | printf("Module has no nets\n"); 212 | return -1; 213 | } 214 | scope = vpi_handle(vpiScope, module); 215 | if (scope) { 216 | sig_iter = vpi_iterate (vpiNet, scope); 217 | if (sig_iter) { 218 | while ((sig = vpi_scan (sig_iter)) != NULL) { 219 | name = vpi_get_str(vpiName, sig); 220 | // Don't export when prefixed 'vpi_' 221 | if (strncmp(name, "vpi_", 4) == 0) { 222 | printf("Setting VPI ctrl signal\n"); 223 | s_vpictrl = sig_iter; // XXX HACK 224 | } else { 225 | t = property_from_signal(root, sig); 226 | if (t == TOKEN_INVALID) { 227 | fprintf(stderr, "Signal '%s' not exported\n", name); 228 | } 229 | } 230 | } 231 | } 232 | } 233 | return 0; 234 | } 235 | 236 | void *netpp_thread(void *arg) 237 | { 238 | int error; 239 | char *argv[] = { 240 | "", (char *) arg 241 | }; 242 | error = start_server(1, argv); 243 | if (error < 0) return 0; 244 | return (void *) 1; 245 | } 246 | 247 | #ifdef __WIN32__ 248 | HANDLE g_thread; 249 | #else 250 | pthread_t g_thread; 251 | #endif 252 | s_cb_data run_cb; 253 | 254 | 255 | int run(struct t_cb_data *cb) 256 | { 257 | s_vpi_time time; 258 | 259 | vpi_get_time(NULL, &time); 260 | printf("Run: Cur time: %d %d\n", time.low, time.high); 261 | 262 | return 0; 263 | } 264 | 265 | int initialize(struct t_cb_data *cb) 266 | { 267 | int error = 0; 268 | 269 | s_vpi_time time = { 270 | .type = vpiSimTime, 271 | }; 272 | 273 | // Init for some dynamic properties: 274 | netpp_root_init("VPI_GHDLwrapper"); 275 | scan(cb); 276 | 277 | /* 278 | vpi_get_time(NULL, &time); 279 | printf("Cur time: %d %d\n", time.low, time.high); 280 | time.low += 200000; 281 | 282 | run_cb.reason = cbReadOnlySynch; 283 | run_cb.cb_rtn = &run; 284 | run_cb.obj = 0; 285 | run_cb.value = 0; 286 | run_cb.time = &time; // &time; 287 | run_cb.user_data = 0; 288 | 289 | if (vpi_register_cb(&run_cb) == NULL) 290 | vpi_printf("cannot register ReadOnlySync call back\n"); 291 | */ 292 | error = pthread_create(&g_thread, NULL, &netpp_thread, NULL); 293 | 294 | return error; 295 | } 296 | 297 | 298 | s_cb_data init_cb; 299 | 300 | void my_handle_register() 301 | { 302 | init_cb.reason = cbEndOfCompile; 303 | init_cb.cb_rtn = &initialize; 304 | init_cb.user_data = 0; 305 | 306 | if (vpi_register_cb(&init_cb) == NULL) 307 | vpi_printf("cannot register EndOfCompile call back\n"); 308 | } 309 | 310 | void (*vlog_startup_routines[]) () = 311 | { 312 | my_handle_register, 313 | 0 314 | }; 315 | -------------------------------------------------------------------------------- /src/vpiwrapper.h: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /thread.c: -------------------------------------------------------------------------------- 1 | /** \file thread.c 2 | * 3 | * Thread demo for GHDL C simulator interface 4 | * (c) 2011, Martin Strubel 5 | * 6 | * Compile with -DUSE_NETPP if you want to use the netpp server 7 | * interface. 8 | * 9 | */ 10 | 11 | #ifdef __linux__ 12 | #include 13 | #endif 14 | #include 15 | #include 16 | 17 | #include "ghpi.h" 18 | #include "example.h" 19 | #include "fifo.h" 20 | 21 | #ifdef USE_NETPP 22 | // #include "example.h" 23 | #include "slave.h" 24 | #include "netpp.h" 25 | #else 26 | #define DCERR_COMM_TIMEOUT -1 27 | #endif 28 | 29 | #define FIFO_SIZE 32*1024 30 | 31 | 32 | #ifdef USE_NETPP 33 | 34 | int init_backend(void) 35 | { 36 | register_proplist(g_devices, g_ndevices); 37 | init_registermap(); 38 | return 0; 39 | } 40 | 41 | extern TOKEN g_t_fifobuf; 42 | extern TOKEN g_t_fifo_infill; 43 | extern TOKEN g_t_fifo_outfill; 44 | /** Legacy global FIFO */ 45 | struct duplexfifo_t g_dfifo; 46 | 47 | #ifdef __WIN32__ 48 | #define THREAD_RETURN DWORD 49 | #define THREAD_DECO WINAPI 50 | #else 51 | #define THREAD_RETURN void * 52 | #define THREAD_DECO 53 | #endif 54 | 55 | THREAD_RETURN THREAD_DECO fifo_thread(void *arg) 56 | { 57 | PropertyDesc *fifo; 58 | int error; 59 | char *argv[] = { 60 | "", (char *) arg 61 | }; 62 | 63 | error = init_backend(); 64 | if (error < 0) return 0; 65 | 66 | // HACK: 67 | // The FIFO buffer is using CUSTOM handlers. For the global FIFO, 68 | // we need to pre-initialize them: 69 | fifo = getProperty_ByToken(g_t_fifobuf); // Obtain descriptor 70 | fifo->access.custom.p = &g_dfifo; // Store FIFO handle 71 | 72 | fifo = getProperty_ByToken(g_t_fifo_infill); // Obtain descriptor 73 | fifo->access.custom.p = &g_dfifo; // Store FIFO handle 74 | 75 | fifo = getProperty_ByToken(g_t_fifo_outfill); // Obtain descriptor 76 | fifo->access.custom.p = &g_dfifo; // Store FIFO handle 77 | 78 | 79 | error = start_server(1, argv); 80 | if (error < 0) return 0; 81 | return (THREAD_RETURN) 1; 82 | } 83 | 84 | #else 85 | 86 | enum { 87 | FROM_SIM, 88 | TO_SIM 89 | }; 90 | 91 | void *fifo_thread(void *arg) 92 | { 93 | int n; 94 | int error; 95 | 96 | static 97 | unsigned char buf[FIFO_SIZE]; 98 | 99 | char flags[6]; 100 | 101 | static 102 | unsigned char seq[] = "Don't you wanna know what's cool?"; 103 | 104 | int i = 3; 105 | 106 | Fifo *fifos = (Fifo *) arg; 107 | 108 | while (i--) { 109 | usleep(1000); 110 | fifo_status(&fifos[TO_SIM], FIFO_WRITE, 1, flags); 111 | 112 | if (flags[TXF] == HIGH) { 113 | n = fifo_write(&fifos[TO_SIM], seq, sizeof(seq)); 114 | if (n > 0) { 115 | error = fifo_blocking_read(&fifos[FROM_SIM], buf, n); 116 | if (error < 0) { 117 | printf("Timed out\n"); 118 | } else { 119 | printf("Return %d bytes from Simulator:\n", n); 120 | printf("%s\n", buf); 121 | hexdump((char *) buf, n); 122 | } 123 | } 124 | } else { 125 | printf("FIFO to Sim not ready. Skipping.\n"); 126 | } 127 | #ifdef USE_NETPP 128 | usleep(g_timeout); 129 | #endif 130 | } 131 | // Send TERMINATE command: 132 | // This is a bit dirty. We have to send two bytes, because we're 133 | // polling the RXAE (almost empty) flag from the VHDL code. 134 | // If just one byte resides in the FIFO, only the RXE flag is high. 135 | n = fifo_write(&fifos[TO_SIM], (unsigned char *) "\377\000", 2); 136 | 137 | return 0; 138 | } 139 | 140 | 141 | #endif 142 | 143 | #ifdef __WIN32__ 144 | HANDLE g_thread; 145 | #else 146 | pthread_t g_thread; 147 | #endif 148 | 149 | /* XXX Legacy. Will leave in future */ 150 | 151 | #include "netppwrap.h" 152 | 153 | int sim_fifo_thread_init(struct ghdl_string *str, int wordsize) 154 | { 155 | int error; 156 | 157 | netpp_root_init("Legacy_Fifo_Thread"); 158 | 159 | 160 | error = fifo_init(&g_dfifo.out, FIFO_SIZE, wordsize); 161 | if (error < 0) return error; 162 | error = fifo_init(&g_dfifo.in, FIFO_SIZE, wordsize); 163 | if (error < 0) return error; 164 | 165 | #ifdef __WIN32__ 166 | DWORD thid; 167 | g_thread = CreateThread(NULL, 0x20000, fifo_thread, (PVOID) &g_dfifo, 168 | 0, &thid); 169 | if (!g_thread) { 170 | error = -1; 171 | printf("Failed to create thread\n"); 172 | } 173 | #else 174 | 175 | #ifdef USE_NETPP 176 | error = pthread_create(&g_thread, NULL, &fifo_thread, NULL); 177 | #else 178 | error = pthread_create(&g_thread, NULL, &fifo_thread, &g_dfifo); 179 | #endif 180 | #endif 181 | if (error < 0) return error; 182 | return 0; 183 | } 184 | 185 | 186 | void fifo_thread_exit() 187 | { 188 | int error; 189 | #ifdef __WIN32__ 190 | error = TerminateThread(g_thread, -1); 191 | if (error == 0) { printf("Failed to terminate thread\n"); } 192 | #else 193 | error = pthread_cancel(g_thread); 194 | #endif 195 | fifo_exit(&g_dfifo.out); 196 | fifo_exit(&g_dfifo.in); 197 | } 198 | --------------------------------------------------------------------------------