URL
https://opencores.org/ocsvn/lxp32/lxp32/trunk
Subversion Repositories lxp32
Compare Revisions
- This comparison shows the changes necessary to convert path
/
- from Rev 4 to Rev 5
- ↔ Reverse comparison
Rev 4 → Rev 5
/lxp32/tags/1.0/tools/.gitignore
0,0 → 1,2
/bin |
/build |
/lxp32/tags/1.0/tools/src/lxp32asm/utils.cpp
0,0 → 1,113
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Utils namespace. |
*/ |
|
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <algorithm> |
#include <stdexcept> |
#include <cstring> |
|
std::string Utils::urlEncode(const std::string &str) { |
std::string res; |
for(std::size_t i=0;i<str.size();i++) { |
char ch=str[i]; |
if(ch>='A'&&ch<='Z') res.push_back(ch); |
else if(ch>='a'&&ch<='z') res.push_back(ch); |
else if(ch>='0'&&ch<='9') res.push_back(ch); |
else if(ch=='-'||ch=='_'||ch=='.'||ch=='~') res.push_back(ch); |
else res+="%"+hex(ch); |
} |
return res; |
} |
|
std::string Utils::urlDecode(const std::string &str) { |
std::string res; |
for(std::size_t i=0;i<str.size();i++) { |
char ch=str[i]; |
if(ch!='%') res.push_back(ch); |
else { |
auto hexcode=str.substr(i+1,2); |
i+=hexcode.size(); |
try { |
if(hexcode.size()!=2) throw std::exception(); |
auto u=static_cast<unsigned char>(std::stoul(hexcode,nullptr,16)); |
res.push_back(static_cast<char>(u)); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed URL-encoded string"); |
} |
} |
} |
return res; |
} |
|
std::string Utils::normalizeSeparators(const std::string &path) { |
std::string str(path); |
#ifdef _WIN32 |
std::replace(str.begin(),str.end(),'\\','/'); |
#endif |
return str; |
} |
|
std::string Utils::nativeSeparators(const std::string &path) { |
std::string str(path); |
#ifdef _WIN32 |
std::replace(str.begin(),str.end(),'/','\\'); |
#endif |
return str; |
} |
|
bool Utils::isAbsolutePath(const std::string &path) { |
auto native=nativeSeparators(path); |
if(native.empty()) return false; |
if(native[0]=='/') return true; |
#ifdef _WIN32 |
if(native.size()>1&&native[1]==':') return true; |
#endif |
return false; |
} |
|
bool Utils::fileExists(const std::string &path) { |
std::ifstream in(nativeSeparators(path),std::ios_base::in); |
if(!in) return false; |
return true; |
} |
|
std::string Utils::relativePath(const std::string &from,const std::string &to) { |
// Normalize directory separators |
auto nfrom=normalizeSeparators(from); |
auto nto=normalizeSeparators(to); |
|
if(nto.empty()) return std::string(); |
|
// If "nto" is an absolute path, just return it |
if(isAbsolutePath(nto)) return nativeSeparators(nto); |
|
// Process relative path |
auto pos=nfrom.find_last_of('/'); |
if(pos==std::string::npos) return nativeSeparators(nto); |
else return nativeSeparators(nfrom.substr(0,pos+1)+nto); |
} |
|
std::string Utils::dequoteString(const std::string &str) { |
if(str.size()<2) throw std::runtime_error("String literal expected"); |
if(str.front()!='\"'||str.back()!='\"') throw std::runtime_error("String literal expected"); |
return str.substr(1,str.size()-2); |
} |
|
bool Utils::ishexdigit(char ch) { |
static const char *digits="0123456789ABCDEFabcdef"; |
return (std::strchr(digits,ch)!=NULL); |
} |
|
bool Utils::isoctdigit(char ch) { |
static const char *digits="01234567"; |
return (std::strchr(digits,ch)!=NULL); |
} |
/lxp32/tags/1.0/tools/src/lxp32asm/assembler.h
0,0 → 1,103
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Assembler class which performs |
* compilation of LXP32 assembly source files. |
*/ |
|
#ifndef ASSEMBLER_H_INCLUDED |
#define ASSEMBLER_H_INCLUDED |
|
#include "linkableobject.h" |
|
#include <vector> |
#include <map> |
#include <string> |
#include <cstdint> |
|
class Assembler { |
typedef std::vector<std::string> TokenList; |
typedef std::int_least64_t Integer; |
enum LexerState { |
Initial, |
Word, |
StringLiteral, |
BlockComment |
}; |
struct Operand { |
enum Type {Null,Register,Identifier,NumericLiteral}; |
Type type=Null; |
std::string str; |
Integer i=0; |
std::uint8_t reg=0; |
}; |
|
LinkableObject _obj; |
std::map<std::string,TokenList> _macros; |
LexerState _state; |
int _line; |
std::vector<std::string> _currentLabels; |
std::string _currentFileName; |
std::vector<std::string> _includeSearchDirs; |
public: |
void processFile(const std::string &filename); |
|
void addIncludeSearchDir(const std::string &dir); |
|
int line() const; |
std::string currentFileName() const; |
|
LinkableObject &object(); |
const LinkableObject &object() const; |
private: |
void processFileRecursive(const std::string &filename); |
TokenList tokenize(const std::string &str); |
void expand(TokenList &list); |
void elaborate(TokenList &list); |
|
void elaborateDirective(TokenList &list); |
LinkableObject::Word elaborateDataDefinition(TokenList &list); |
LinkableObject::Word elaborateInstruction(TokenList &list); |
|
static bool validateIdentifier(const std::string &str); |
static Integer numericLiteral(const std::string &str); |
static std::vector<Operand> getOperands(const TokenList &list); |
|
// LXP32 instructions |
void encodeDstOperand(LinkableObject::Word &word,const Operand &arg); |
void encodeRd1Operand(LinkableObject::Word &word,const Operand &arg); |
void encodeRd2Operand(LinkableObject::Word &word,const Operand &arg); |
|
void encodeAdd(const TokenList &list); |
void encodeAnd(const TokenList &list); |
void encodeCall(const TokenList &list); |
void encodeCjmpxx(const TokenList &list); |
void encodeDivs(const TokenList &list); |
void encodeDivu(const TokenList &list); |
void encodeHlt(const TokenList &list); |
void encodeJmp(const TokenList &list); |
void encodeIret(const TokenList &list); |
void encodeLc(const TokenList &list); |
void encodeLsb(const TokenList &list); |
void encodeLub(const TokenList &list); |
void encodeLw(const TokenList &list); |
void encodeMods(const TokenList &list); |
void encodeModu(const TokenList &list); |
void encodeMov(const TokenList &list); |
void encodeMul(const TokenList &list); |
void encodeNop(const TokenList &list); |
void encodeNot(const TokenList &list); |
void encodeOr(const TokenList &list); |
void encodeRet(const TokenList &list); |
void encodeSb(const TokenList &list); |
void encodeSl(const TokenList &list); |
void encodeSrs(const TokenList &list); |
void encodeSru(const TokenList &list); |
void encodeSub(const TokenList &list); |
void encodeSw(const TokenList &list); |
void encodeXor(const TokenList &list); |
}; |
|
#endif |
/lxp32/tags/1.0/tools/src/lxp32asm/outputwriter.h
0,0 → 1,61
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the OutputWriter abstract class and its |
* derived classes. These classes are used to write LXP32 executable |
* code in different formats. |
*/ |
|
#ifndef OUTPUTWRITER_H_INCLUDED |
#define OUTPUTWRITER_H_INCLUDED |
|
#include <fstream> |
#include <string> |
|
/* |
* An abstract base class for all writers |
*/ |
|
class OutputWriter { |
public: |
virtual ~OutputWriter() {} |
virtual void write(const char *data,std::size_t n)=0; |
virtual void abort() {} |
void pad(std::size_t size); |
}; |
|
/* |
* Write a regular binary file |
*/ |
|
class BinaryOutputWriter : public OutputWriter { |
std::string _filename; |
std::ofstream _os; |
public: |
BinaryOutputWriter(const std::string &filename); |
virtual void write(const char *data,std::size_t n) override; |
virtual void abort() override; |
}; |
|
/* |
* Write a text file (one word per line) |
*/ |
|
class TextOutputWriter : public OutputWriter { |
public: |
enum Format {Bin,Dec,Hex}; |
private: |
std::string _filename; |
std::ofstream _os; |
std::string _buf; |
Format _fmt; |
public: |
TextOutputWriter(const std::string &filename,Format f); |
~TextOutputWriter(); |
virtual void write(const char *data,std::size_t n) override; |
virtual void abort() override; |
}; |
|
#endif |
/lxp32/tags/1.0/tools/src/lxp32asm/linker.cpp
0,0 → 1,159
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Linker class. |
*/ |
|
#include "linker.h" |
|
#include "linkableobject.h" |
|
#include <iostream> |
#include <fstream> |
#include <sstream> |
#include <map> |
#include <stdexcept> |
#include <cassert> |
|
void Linker::addObject(LinkableObject &obj) { |
_objects.push_back(&obj); |
} |
|
void Linker::link(OutputWriter &writer) { |
if(_objects.empty()) throw std::runtime_error("Object set is empty"); |
|
// Merge symbol tables |
buildSymbolTable(); |
|
// Determine entry point |
if(_objects.size()==1) _entryObject=_objects[0]; |
else { |
auto const it=_globalSymbolTable.find("entry"); |
if(it==_globalSymbolTable.end()) |
throw std::runtime_error("Entry point not defined: cannot find \"entry\" symbol"); |
if(it->second.rva!=0) |
throw std::runtime_error(it->second.obj->name()+": Entry point must refer to the start of the object"); |
_entryObject=it->second.obj; |
} |
|
// Assign virtual addresses |
placeObjects(); |
|
// Perform relocations |
for(auto &obj: _objects) relocateObject(obj); |
|
// Write binary data |
writeObjects(writer); |
} |
|
void Linker::setBase(LinkableObject::Word base) { |
_base=base; |
} |
|
void Linker::setAlignment(std::size_t align) { |
_align=align; |
} |
|
void Linker::setImageSize(std::size_t size) { |
_imageSize=size; |
} |
|
/* |
* Private members |
*/ |
|
void Linker::buildSymbolTable() { |
_globalSymbolTable.clear(); |
|
for(auto const &obj: _objects) { |
auto const &table=obj->symbols(); |
for(auto const &item: table) { |
// Insert item to the global symbol table if it doesn't exist yet |
auto it=_globalSymbolTable.emplace(item.first,GlobalSymbolData()).first; |
|
// If the symbol is local, check that it has not been already defined in another object |
if(item.second.type==LinkableObject::Local) { |
if(it->second.obj) { |
std::ostringstream msg; |
msg<<obj->name()<<": Duplicate definition of \""<<item.first; |
msg<<"\" (previously defined in "<<it->second.obj->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
it->second.obj=obj; |
it->second.rva=item.second.rva; |
} |
|
// Merge reference tables |
for(auto const &ref: item.second.refs) it->second.refs.emplace(obj,ref.rva); |
} |
} |
|
// Check that no undefined symbols remain |
for(auto const &item: _globalSymbolTable) { |
if(item.second.obj==nullptr&&!item.second.refs.empty()) { |
std::ostringstream msg; |
msg<<"Undefined symbol: \""<<item.first<<"\""; |
auto const it=item.second.refs.begin(); |
msg<<" (referenced from "<<it->first->name()<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
} |
|
void Linker::placeObjects() { |
auto currentBase=_base; |
|
// Make entry object the first |
if(_objects.size()>1) { |
for(auto it=_objects.begin();it!=_objects.end();++it) { |
if(*it==_entryObject) { |
std::swap(*it,_objects[0]); |
break; |
} |
} |
} |
|
// Set base addresses |
for(auto it=_objects.begin();it!=_objects.end();++it) { |
(*it)->setVirtualAddress(currentBase); |
if(it+1!=_objects.end()) (*it)->addPadding(_align); |
else (*it)->addPadding(); |
currentBase+=static_cast<LinkableObject::Word>((*it)->codeSize()); |
} |
} |
|
void Linker::relocateObject(LinkableObject *obj) { |
for(auto const &sym: obj->symbols()) { |
auto it=_globalSymbolTable.find(sym.first); |
assert(it!=_globalSymbolTable.end()); |
if(it->second.refs.empty()) continue; |
assert(it->second.obj); |
auto addr=it->second.obj->virtualAddress()+it->second.rva; |
for(auto const &ref: sym.second.refs) { |
auto offset=obj->getWord(ref.rva); |
obj->replaceWord(ref.rva,addr+offset); |
} |
} |
} |
|
void Linker::writeObjects(OutputWriter &writer) { |
std::size_t currentSize=0; |
// Write entry object |
writer.write(reinterpret_cast<const char*>(_entryObject->code()),_entryObject->codeSize()); |
currentSize+=_entryObject->codeSize(); |
// Write other objects |
for(auto const &obj: _objects) { |
if(obj==_entryObject) continue; |
writer.write(reinterpret_cast<const char*>(obj->code()),obj->codeSize()); |
currentSize+=obj->codeSize(); |
} |
|
// Pad file if requested |
if(_imageSize>0) { |
if(currentSize>_imageSize) |
throw std::runtime_error("Image size exceeds the specified value"); |
else if(currentSize<_imageSize) writer.pad(_imageSize-currentSize); |
} |
} |
/lxp32/tags/1.0/tools/src/lxp32asm/main.cpp
0,0 → 1,285
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* Main translation unit for the LXP32 assembler/linker. |
*/ |
|
#include "assembler.h" |
#include "linker.h" |
|
#include <iostream> |
#include <fstream> |
#include <vector> |
#include <string> |
#include <exception> |
#include <utility> |
#include <memory> |
#include <cstdlib> |
#include <cstring> |
#include <cassert> |
|
struct Options { |
enum OutputFormat {Bin,Textio,Dec,Hex}; |
|
bool compileOnly=false; |
std::string outputFileName; |
std::vector<std::string> includeSearchDirs; |
LinkableObject::Word base=0; |
std::size_t align=4; |
std::size_t imageSize=0; |
OutputFormat fmt=Bin; |
}; |
|
static void displayUsage(std::ostream &os,const char *program) { |
os<<std::endl; |
os<<"Usage:"<<std::endl; |
os<<" "<<program<<" [ option(s) | input file(s) ]"<<std::endl<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -a <align> Section alignment (default: 4)"<<std::endl; |
os<<" -b <addr> Base address (default: 0)"<<std::endl; |
os<<" -c Compile only (don't link)"<<std::endl; |
os<<" -f <fmt> Output file format (see below)"<<std::endl; |
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -i <dir> Add directory to the list of directories used to search"<<std::endl; |
os<<" for included files (multiple directories can be specified)"<<std::endl; |
os<<" -o <file> Output file name"<<std::endl; |
os<<" -s <size> Output image size"<<std::endl; |
os<<" -- Do not interpret subsequent arguments as options"<<std::endl; |
os<<std::endl; |
os<<"Section alignment and image size must be multiples of 4."<<std::endl; |
os<<"Base address must be a multiple of section alignment."<<std::endl; |
os<<std::endl; |
|
os<<"Output file formats:"<<std::endl; |
os<<" bin Raw binary image (default)"<<std::endl; |
os<<" textio Text representation of binary data. Supported by"<<std::endl; |
os<<" std.textio (VHDL) and $readmemb (Verilog)"<<std::endl; |
os<<" dec Text format, one word per line (decimal)"<<std::endl; |
os<<" hex Text format, one word per line (hexadecimal)"<<std::endl; |
} |
|
static bool isLinkableObject(const std::string &filename) { |
static const char *id="LinkableObject"; |
static std::size_t idSize=std::strlen(id); |
|
std::ifstream in(filename,std::ios_base::in); |
if(!in) return false; |
|
std::vector<char> buf(idSize); |
in.read(buf.data(),idSize); |
if(static_cast<std::size_t>(in.gcount())!=idSize) return false; |
if(std::memcmp(buf.data(),id,idSize)) return false; |
return true; |
} |
|
int main(int argc,char *argv[]) try { |
std::vector<std::string> inputFiles; |
Options options; |
bool alignmentSpecified=false; |
bool baseSpecified=false; |
bool formatSpecified=false; |
bool noMoreOptions=false; |
|
std::cout<<"LXP32 Platform Assembler and Linker"<<std::endl; |
std::cout<<"Copyright (c) 2016 by Alex I. Kuznetsov"<<std::endl; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
|
for(int i=1;i<argc;i++) { |
if(argv[i][0]!='-'||noMoreOptions) inputFiles.push_back(argv[i]); |
else if(!strcmp(argv[i],"--")) noMoreOptions=true; |
else if(!strcmp(argv[i],"-a")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
options.align=std::stoul(argv[i],nullptr,0); |
if(options.align%4!=0||options.align==0) throw std::exception(); |
alignmentSpecified=true; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid section alignment"); |
} |
} |
else if(!strcmp(argv[i],"-b")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
options.base=std::stoul(argv[i],nullptr,0); |
//if(options.base%4!=0) throw std::exception(); |
baseSpecified=true; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid base address"); |
} |
} |
else if(!strcmp(argv[i],"-c")) { |
options.compileOnly=true; |
} |
else if(!strcmp(argv[i],"-f")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
if(!strcmp(argv[i],"bin")) options.fmt=Options::Bin; |
else if(!strcmp(argv[i],"textio")) options.fmt=Options::Textio; |
else if(!strcmp(argv[i],"dec")) options.fmt=Options::Dec; |
else if(!strcmp(argv[i],"hex")) options.fmt=Options::Hex; |
else throw std::runtime_error("Unrecognized output format"); |
formatSpecified=true; |
} |
else if(!strcmp(argv[i],"-h")||!strcmp(argv[i],"--help")) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
else if(!strcmp(argv[i],"-i")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
options.includeSearchDirs.push_back(argv[i]); |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
options.outputFileName=argv[i]; |
} |
else if(!strcmp(argv[i],"-s")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
options.imageSize=std::stoul(argv[i],nullptr,0); |
if(options.imageSize%4!=0||options.imageSize==0) throw std::exception(); |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid image size"); |
} |
} |
else throw std::runtime_error(std::string("Unrecognized option: \"")+argv[i]+"\""); |
} |
|
if(options.base%options.align!=0) |
throw std::runtime_error("Base address must be a multiple of section alignment"); |
|
if(options.compileOnly) { |
if(alignmentSpecified) |
std::cerr<<"Warning: Section alignment is ignored in compile-only mode"<<std::endl; |
if(baseSpecified) |
std::cerr<<"Warning: Base address is ignored in compile-only mode"<<std::endl; |
if(formatSpecified) |
std::cerr<<"Warning: Output format is ignored in compile-only mode"<<std::endl; |
if(options.imageSize>0) |
std::cerr<<"Warning: Image size is ignored in compile-only mode"<<std::endl; |
} |
|
if(inputFiles.empty()) |
throw std::runtime_error("No input files were specified"); |
|
if(options.compileOnly&&inputFiles.size()>1&&!options.outputFileName.empty()) |
throw std::runtime_error("Output file name cannot be specified " |
"for multiple files in compile-only mode"); |
|
std::vector<Assembler> assemblers; |
std::vector<LinkableObject> rawObjects; |
|
for(auto const &filename: inputFiles) { |
if(options.compileOnly||!isLinkableObject(filename)) { |
Assembler as; |
for(auto const &dir: options.includeSearchDirs) as.addIncludeSearchDir(dir); |
try { |
as.processFile(filename); |
} |
catch(std::exception &ex) { |
std::cerr<<"Assembler error in "<<as.currentFileName(); |
if(as.line()>0) std::cerr<<":"<<as.line(); |
std::cerr<<": "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
if(!options.compileOnly) assemblers.push_back(std::move(as)); |
else { |
std::string outputFileName=options.outputFileName; |
if(outputFileName.empty()) { |
outputFileName=filename; |
auto pos=outputFileName.find_last_of('.'); |
if(pos!=std::string::npos) outputFileName.erase(pos); |
outputFileName+=".lo"; |
} |
as.object().serialize(outputFileName); |
} |
} |
else { |
LinkableObject lo; |
try { |
lo.deserialize(filename); |
} |
catch(std::exception &ex) { |
std::cerr<<"Error reading object file "<<filename<<": "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
rawObjects.push_back(std::move(lo)); |
} |
} |
|
if(options.compileOnly) return 0; |
|
Linker linker; |
for(auto &lo: rawObjects) linker.addObject(lo); |
for(auto &as: assemblers) linker.addObject(as.object()); |
linker.setBase(options.base); |
linker.setAlignment(options.align); |
linker.setImageSize(options.imageSize); |
|
std::string outputFileName=options.outputFileName; |
if(outputFileName.empty()) { |
outputFileName=inputFiles[0]; |
auto pos=outputFileName.find_last_of('.'); |
if(pos!=std::string::npos) outputFileName.erase(pos); |
if(options.fmt==Options::Bin) outputFileName+=".bin"; |
else outputFileName+=".txt"; |
} |
|
std::unique_ptr<OutputWriter> writer; |
|
switch(options.fmt) { |
case Options::Bin: |
writer=std::unique_ptr<OutputWriter>(new BinaryOutputWriter(outputFileName)); |
break; |
case Options::Textio: |
writer=std::unique_ptr<OutputWriter>(new TextOutputWriter(outputFileName,TextOutputWriter::Bin)); |
break; |
case Options::Dec: |
writer=std::unique_ptr<OutputWriter>(new TextOutputWriter(outputFileName,TextOutputWriter::Dec)); |
break; |
case Options::Hex: |
writer=std::unique_ptr<OutputWriter>(new TextOutputWriter(outputFileName,TextOutputWriter::Hex)); |
break; |
default: |
assert(false); |
} |
|
try { |
linker.link(*writer); |
} |
catch(std::exception &ex) { |
writer->abort(); |
std::cerr<<"Linker error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
/lxp32/tags/1.0/tools/src/lxp32asm/utils.h
0,0 → 1,57
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module declares the members of the Utils namespace. |
*/ |
|
#ifndef UTILS_H_INCLUDED |
#define UTILS_H_INCLUDED |
|
#include <string> |
#include <type_traits> |
|
namespace Utils { |
template <typename T> std::string hex(const T &w) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
const char *hexstr="0123456789ABCDEF"; |
std::string res; |
|
res.reserve(sizeof(T)*2); |
|
for(int i=sizeof(T)*8-4;i>=0;i-=4) { |
res.push_back(hexstr[(w>>i)&0x0F]); |
} |
return res; |
} |
|
template <typename T> std::string bin(const T &w) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
std::string res; |
|
res.reserve(sizeof(T)*8); |
|
for(int i=sizeof(T)*8-1;i>=0;i--) { |
if(((w>>i)&1)!=0) res.push_back('1'); |
else res.push_back('0'); |
} |
return res; |
} |
|
std::string urlEncode(const std::string &str); |
std::string urlDecode(const std::string &str); |
|
std::string normalizeSeparators(const std::string &path); |
std::string nativeSeparators(const std::string &path); |
bool isAbsolutePath(const std::string &path); |
bool fileExists(const std::string &path); |
std::string relativePath(const std::string &from,const std::string &to); |
|
std::string dequoteString(const std::string &str); |
|
bool ishexdigit(char ch); |
bool isoctdigit(char ch); |
} |
|
#endif |
/lxp32/tags/1.0/tools/src/lxp32asm/linkableobject.cpp
0,0 → 1,265
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the LinkableObject class. |
*/ |
|
#include "linkableobject.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <stdexcept> |
#include <utility> |
#include <cassert> |
#include <cstdlib> |
|
std::string LinkableObject::name() const { |
return _name; |
} |
|
void LinkableObject::setName(const std::string &str) { |
_name=str; |
} |
|
LinkableObject::Word LinkableObject::virtualAddress() const { |
return _virtualAddress; |
} |
|
void LinkableObject::setVirtualAddress(Word addr) { |
_virtualAddress=addr; |
} |
|
LinkableObject::Byte *LinkableObject::code() { |
return _code.data(); |
} |
|
const LinkableObject::Byte *LinkableObject::code() const { |
return _code.data(); |
} |
|
std::size_t LinkableObject::codeSize() const { |
return _code.size(); |
} |
|
LinkableObject::Word LinkableObject::addWord(Word w) { |
auto rva=addPadding(sizeof(Word)); |
// Note: this code doesn't depend on host machine's endianness |
_code.push_back(static_cast<Byte>(w)); |
_code.push_back(static_cast<Byte>(w>>8)); |
_code.push_back(static_cast<Byte>(w>>16)); |
_code.push_back(static_cast<Byte>(w>>24)); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addByte(Byte b) { |
auto rva=static_cast<LinkableObject::Word>(_code.size()); |
_code.push_back(b); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addBytes(const Byte *p,std::size_t n) { |
auto rva=static_cast<LinkableObject::Word>(_code.size()); |
_code.insert(_code.end(),p,p+n); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addZeros(std::size_t n) { |
auto rva=static_cast<LinkableObject::Word>(_code.size()); |
_code.resize(_code.size()+n); |
return rva; |
} |
|
LinkableObject::Word LinkableObject::addPadding(std::size_t size) { |
auto padding=(size-_code.size()%size)%size; |
if(padding>0) _code.resize(_code.size()+padding); |
return static_cast<LinkableObject::Word>(_code.size()); |
} |
|
LinkableObject::Word LinkableObject::getWord(Word rva) const { |
Word w=0; |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++]); |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++])<<8; |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++])<<16; |
if(rva<codeSize()) w|=static_cast<Word>(_code[rva++])<<24; |
return w; |
} |
|
void LinkableObject::replaceWord(Word rva,Word value) { |
assert(rva+sizeof(Word)<=codeSize()); |
// Note: this code doesn't depend on host machine's endianness |
_code[rva++]=static_cast<Byte>(value); |
_code[rva++]=static_cast<Byte>(value>>8); |
_code[rva++]=static_cast<Byte>(value>>16); |
_code[rva++]=static_cast<Byte>(value>>24); |
} |
|
void LinkableObject::addLocalSymbol(const std::string &name,Word rva) { |
auto &data=symbol(name); |
if(data.type!=Unknown) throw std::runtime_error("Symbol \""+name+"\" is already defined"); |
data.type=Local; |
data.rva=rva; |
} |
|
void LinkableObject::addExternalSymbol(const std::string &name) { |
auto &data=symbol(name); |
if(data.type!=Unknown) throw std::runtime_error("Symbol \""+name+"\" is already defined"); |
data.type=External; |
} |
|
void LinkableObject::addReference(const std::string &symbolName,const std::string &source,int line,Word rva) { |
auto &data=symbol(symbolName); |
data.refs.push_back({source,line,rva}); |
} |
|
LinkableObject::SymbolData &LinkableObject::symbol(const std::string &name) { |
return _symbols[name]; |
} |
|
const LinkableObject::SymbolData &LinkableObject::symbol(const std::string &name) const { |
auto const it=_symbols.find(name); |
if(it==_symbols.end()) throw std::runtime_error("Undefined symbol"); |
return it->second; |
} |
|
const LinkableObject::SymbolTable &LinkableObject::symbols() const { |
return _symbols; |
} |
|
void LinkableObject::serialize(const std::string &filename) const { |
std::ofstream out(filename,std::ios_base::out); |
if(!out) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
|
out<<"LinkableObject"<<std::endl; |
if(!_name.empty()) out<<"Name "<<Utils::urlEncode(_name)<<std::endl; |
out<<"VirtualAddress 0x"<<Utils::hex(_virtualAddress)<<std::endl; |
|
out<<std::endl; |
out<<"Start Code"<<std::endl; |
|
for(Word rva=0;rva<_code.size();rva+=sizeof(Word)) { |
out<<"\t0x"<<Utils::hex(getWord(rva))<<std::endl; |
} |
|
out<<"End Code"<<std::endl; |
|
for(auto const &sym: _symbols) { |
if(sym.second.type==Unknown) |
throw std::runtime_error("Undefined symbol: \""+sym.first+"\""); |
out<<std::endl; |
out<<"Start Symbol"<<std::endl; |
out<<"\tName "<<Utils::urlEncode(sym.first)<<std::endl; |
if(sym.second.type==Local) out<<"\tRVA 0x"<<Utils::hex(sym.second.rva)<<std::endl; |
else out<<"\tExternal"<<std::endl; |
for(auto const &ref: sym.second.refs) { |
out<<"\tRef "<<Utils::urlEncode(ref.source)<<" "<<ref.line<<" 0x"<<Utils::hex(ref.rva)<<std::endl; |
} |
out<<"End Symbol"<<std::endl; |
} |
} |
|
void LinkableObject::deserialize(const std::string &filename) { |
std::ifstream in(filename,std::ios_base::in); |
if(!in) throw std::runtime_error("Cannot open \""+filename+"\""); |
|
operator=(LinkableObject()); |
|
std::string line; |
for(;;) { |
if(!std::getline(in,line)) throw std::runtime_error("Bad object format"); |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
else if(tokens[0]!="LinkableObject") throw std::runtime_error("Bad object format"); |
break; |
} |
|
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
else if(tokens[0]=="Name") _name=Utils::urlDecode(tokens[1]); |
else if(tokens[0]=="VirtualAddress") _virtualAddress=std::strtoul(tokens[1].c_str(),NULL,0); |
else if(tokens[0]=="Start") { |
if(tokens[1]=="Code") deserializeCode(in); |
else if(tokens[1]=="Symbol") deserializeSymbol(in); |
else throw std::runtime_error("Unexpected token: \""+tokens[1]+"\""); |
} |
else throw std::runtime_error("Unexpected token: \""+tokens[0]+"\""); |
} |
} |
|
/* |
* Private members |
*/ |
|
void LinkableObject::deserializeCode(std::istream &in) { |
std::string line; |
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
if(tokens[0]=="End") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
if(tokens[1]=="Code") return; |
throw std::runtime_error("Unexpected token: \""+tokens[1]+"\""); |
} |
auto w=static_cast<Word>(std::strtoul(tokens[0].c_str(),NULL,0)); |
addWord(w); |
} |
throw std::runtime_error("Unexpected end of file"); |
} |
|
void LinkableObject::deserializeSymbol(std::istream &in) { |
std::string line; |
std::string name; |
SymbolData data; |
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
if(tokens.empty()) continue; |
if(tokens[0]=="End") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
if(tokens[1]=="Symbol") { |
if(name.empty()) throw std::runtime_error("Symbol name is not defined"); |
if(data.type==Unknown) throw std::runtime_error("Bad symbol type"); |
_symbols.emplace(std::move(name),std::move(data)); |
return; |
} |
throw std::runtime_error("Unexpected token: \""+tokens[1]+"\""); |
} |
else if(tokens[0]=="Name") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
name=Utils::urlDecode(tokens[1]); |
} |
else if(tokens[0]=="External") data.type=External; |
else if(tokens[0]=="RVA") { |
if(tokens.size()<2) throw std::runtime_error("Unexpected end of line"); |
data.type=Local; |
data.rva=std::strtoul(tokens[1].c_str(),NULL,0); |
} |
else if(tokens[0]=="Ref") { |
Reference ref; |
if(tokens.size()<4) throw std::runtime_error("Unexpected end of line"); |
ref.source=Utils::urlDecode(tokens[1]); |
ref.line=std::strtoul(tokens[2].c_str(),NULL,0); |
ref.rva=std::strtoul(tokens[3].c_str(),NULL,0); |
data.refs.push_back(std::move(ref)); |
} |
} |
throw std::runtime_error("Unexpected end of file"); |
} |
|
std::vector<std::string> LinkableObject::tokenize(const std::string &str) { |
std::vector<std::string> tokens; |
for(std::size_t pos=0;;) { |
auto start=str.find_first_not_of(" \t\r\n",pos); |
if(start==std::string::npos) return tokens; |
auto end=str.find_first_of(" \t\r\n",start); |
if(end==std::string::npos) { |
tokens.push_back(str.substr(start)); |
return tokens; |
} |
else tokens.push_back(str.substr(start,end-start)); |
pos=end; |
} |
} |
/lxp32/tags/1.0/tools/src/lxp32asm/linker.h
0,0 → 1,49
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Linker class which performs |
* linking of LXP32 binary objects. |
*/ |
|
#ifndef LINKER_H_INCLUDED |
#define LINKER_H_INCLUDED |
|
#include "linkableobject.h" |
#include "outputwriter.h" |
|
#include <iostream> |
#include <map> |
#include <vector> |
#include <string> |
|
class Linker { |
struct GlobalSymbolData { |
LinkableObject *obj=nullptr; |
LinkableObject::Word rva=0; |
std::multimap<const LinkableObject*,LinkableObject::Word> refs; |
}; |
|
std::vector<LinkableObject*> _objects; |
LinkableObject *_entryObject; |
std::map<std::string,GlobalSymbolData> _globalSymbolTable; |
|
// Various output options |
LinkableObject::Word _base=0; |
std::size_t _align=4; |
std::size_t _imageSize=0; |
public: |
void addObject(LinkableObject &obj); |
void link(OutputWriter &writer); |
void setBase(LinkableObject::Word base); |
void setAlignment(std::size_t align); |
void setImageSize(std::size_t size); |
private: |
void buildSymbolTable(); |
void placeObjects(); |
void relocateObject(LinkableObject *obj); |
void writeObjects(OutputWriter &writer); |
}; |
|
#endif |
/lxp32/tags/1.0/tools/src/lxp32asm/CMakeLists.txt
0,0 → 1,12
cmake_minimum_required(VERSION 3.3.0) |
|
add_executable(lxp32asm assembler.cpp linkableobject.cpp linker.cpp main.cpp outputwriter.cpp utils.cpp) |
|
if(MSVC) |
# Make the program expand wildcard command-line arguments |
set_target_properties(lxp32asm PROPERTIES LINK_FLAGS "setargv.obj") |
endif() |
|
# Install |
|
install(TARGETS lxp32asm DESTINATION .) |
/lxp32/tags/1.0/tools/src/lxp32asm/assembler.cpp
0,0 → 1,868
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Assembler class. |
*/ |
|
#include "assembler.h" |
#include "utils.h" |
|
#include <iostream> |
#include <fstream> |
#include <sstream> |
#include <stdexcept> |
#include <utility> |
#include <limits> |
#include <type_traits> |
#include <cctype> |
#include <cassert> |
#include <cstdlib> |
|
void Assembler::processFile(const std::string &filename) { |
auto nativePath=Utils::normalizeSeparators(filename); |
auto pos=nativePath.find_last_of('/'); |
if(pos!=std::string::npos) nativePath=filename.substr(pos+1); |
_obj.setName(nativePath); |
|
_line=0; |
_state=Initial; |
_currentFileName=filename; |
processFileRecursive(filename); |
// Examine symbol table |
for(auto const &sym: _obj.symbols()) { |
if(sym.second.type==LinkableObject::Unknown&&!sym.second.refs.empty()) { |
std::ostringstream msg; |
msg<<"Undefined symbol \""+sym.first+"\""; |
msg<<" (referenced from "<<sym.second.refs[0].source; |
msg<<":"<<sym.second.refs[0].line<<")"; |
throw std::runtime_error(msg.str()); |
} |
} |
} |
|
void Assembler::processFileRecursive(const std::string &filename) { |
std::ifstream in(filename,std::ios_base::in); |
if(!in) throw std::runtime_error("Cannot open file \""+filename+"\""); |
|
// Process input file line-by-line |
auto savedLine=_line; |
auto savedState=_state; |
auto savedFileName=_currentFileName; |
|
_line=1; |
_state=Initial; |
_currentFileName=filename; |
|
std::string line; |
while(std::getline(in,line)) { |
auto tokens=tokenize(line); |
expand(tokens); |
elaborate(tokens); |
_line++; |
} |
|
_line=savedLine; |
_state=savedState; |
_currentFileName=savedFileName; |
|
for(auto const &label: _currentLabels) { |
_obj.addLocalSymbol(label, |
static_cast<LinkableObject::Word>(_obj.codeSize())); |
} |
|
_currentLabels.clear(); |
} |
|
void Assembler::addIncludeSearchDir(const std::string &dir) { |
auto ndir=Utils::normalizeSeparators(dir); |
if(!ndir.empty()&&ndir.back()!='/') ndir.push_back('/'); |
_includeSearchDirs.push_back(std::move(ndir)); |
} |
|
int Assembler::line() const { |
return _line; |
} |
|
std::string Assembler::currentFileName() const { |
return _currentFileName; |
} |
|
LinkableObject &Assembler::object() { |
return _obj; |
} |
|
const LinkableObject &Assembler::object() const { |
return _obj; |
} |
|
Assembler::TokenList Assembler::tokenize(const std::string &str) { |
TokenList tokenList; |
std::string word; |
std::size_t i; |
for(i=0;i<str.size();i++) { |
char ch=str[i]; |
switch(_state) { |
case Initial: |
if(ch==' '||ch=='\t'||ch=='\n'||ch=='\r') continue; // skip whitespace |
else if(ch==','||ch==':') { // separator |
tokenList.push_back(std::string(1,ch)); |
} |
else if(std::isalnum(ch)||ch=='.'||ch=='#'||ch=='_'||ch=='-'||ch=='+') { |
word=std::string(1,ch); |
_state=Word; |
} |
else if(ch=='\"') { |
word="\""; |
_state=StringLiteral; |
} |
else if(ch=='/') { |
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line"); |
ch=str[i]; |
if(ch=='/') i=str.size(); // skip the rest of the line |
else if(ch=='*') _state=BlockComment; |
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\""); |
} |
else throw std::runtime_error(std::string("Unexpected character: \"")+ch+"\""); |
break; |
case Word: |
if(std::isalnum(ch)||ch=='_'||ch=='@') word+=ch; |
else { |
i--; |
_state=Initial; |
tokenList.push_back(std::move(word)); |
} |
break; |
case StringLiteral: |
if(ch=='\\') { |
if(++i>=str.size()) throw std::runtime_error("Unexpected end of line"); |
ch=str[i]; |
if(ch=='\\') word.push_back('\\'); |
else if(ch=='\"') word.push_back('\"'); |
else if(ch=='\'') word.push_back('\''); |
else if(ch=='t') word.push_back('\t'); |
else if(ch=='n') word.push_back('\n'); |
else if(ch=='r') word.push_back('\r'); |
else if(ch=='x') { // hexadecimal sequence can be 1-2 digit long |
std::string seq; |
if(i+1<str.size()&&Utils::ishexdigit(str[i+1])) seq+=str[i+1]; |
if(i+2<str.size()&&Utils::ishexdigit(str[i+2])) seq+=str[i+2]; |
if(seq.empty()) throw std::runtime_error("Ill-formed escape sequence"); |
try { |
word.push_back(static_cast<char>(std::stoul(seq,nullptr,16))); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed escape sequence"); |
} |
i+=seq.size(); |
} |
else if(Utils::isoctdigit(ch)) { // octal sequence can be 1-3 digit long |
std::string seq(1,ch); |
if(i+1<str.size()&&Utils::isoctdigit(str[i+1])) seq+=str[i+1]; |
if(i+2<str.size()&&Utils::isoctdigit(str[i+2])) seq+=str[i+2]; |
unsigned long value; |
try { |
value=std::stoul(seq,nullptr,8); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed escape sequence"); |
} |
|
if(value>255) throw std::runtime_error("Octal value is out of range"); |
word.push_back(static_cast<char>(value)); |
|
i+=seq.size()-1; |
} |
else throw std::runtime_error(std::string("Unknown escape sequence: \"\\")+ch+"\""); |
} |
else if(ch=='\"') { |
word.push_back('\"'); |
tokenList.push_back(std::move(word)); |
_state=Initial; |
} |
else word.push_back(ch); |
break; |
case BlockComment: |
if(ch=='*') { |
if(++i>=str.size()) break; |
ch=str[i]; |
if(ch=='/') _state=Initial; |
else i--; |
} |
break; |
} |
} |
|
if(_state==StringLiteral) throw std::runtime_error("Unexpected end of line"); |
if(_state==Word) tokenList.push_back(std::move(word)); // store last word |
if(_state!=BlockComment) _state=Initial; // reset state if not in block comment |
|
return tokenList; |
} |
|
void Assembler::expand(TokenList &list) { |
TokenList newlist; |
// Perform macro substitution |
for(auto &token: list) { |
auto it=_macros.find(token); |
if(it==_macros.end()) newlist.push_back(std::move(token)); |
else for(auto const &replace: it->second) newlist.push_back(replace); |
} |
list=std::move(newlist); |
} |
|
void Assembler::elaborate(TokenList &list) { |
if(list.empty()) return; |
|
// Process label (if present) |
if(list.size()>=2&&list[1]==":") { |
if(!validateIdentifier(list[0])) |
throw std::runtime_error("Ill-formed identifier: \""+list[0]+"\""); |
_currentLabels.push_back(std::move(list[0])); |
list.erase(list.begin(),list.begin()+2); |
} |
|
if(list.empty()) return; |
|
// Process statement itself |
if(list[0][0]=='#') elaborateDirective(list); |
else { |
LinkableObject::Word rva; |
if(list[0][0]=='.') rva=elaborateDataDefinition(list); |
else rva=elaborateInstruction(list); |
|
for(auto const &label: _currentLabels) { |
_obj.addLocalSymbol(label,rva); |
} |
_currentLabels.clear(); |
} |
} |
|
void Assembler::elaborateDirective(TokenList &list) { |
assert(!list.empty()); |
|
if(list[0]=="#define") { |
if(list.size()<3) throw std::runtime_error("Wrong number of tokens in the directive"); |
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_macros.emplace(list[1],TokenList(list.begin()+2,list.end())); |
} |
else if(list[0]=="#extern") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
if(!validateIdentifier(list[1])) throw std::runtime_error("Ill-formed identifier: \""+list[1]+"\""); |
_obj.addExternalSymbol(list[1]); |
} |
else if(list[0]=="#include") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
auto filename=Utils::dequoteString(list[1]); |
if(Utils::isAbsolutePath(filename)) return processFileRecursive(filename); |
else { |
auto path=Utils::relativePath(currentFileName(),filename); |
if(Utils::fileExists(path)) return processFileRecursive(path); |
else { |
for(auto const &dir: _includeSearchDirs) { |
path=Utils::nativeSeparators(dir+filename); |
if(Utils::fileExists(path)) return processFileRecursive(path); |
} |
} |
} |
throw std::runtime_error("Cannot locate include file \""+filename+"\""); |
} |
else if(list[0]=="#message") { |
if(list.size()!=2) std::runtime_error("Wrong number of tokens in the directive"); |
auto msg=Utils::dequoteString(list[1]); |
std::cout<<currentFileName()<<":"<<line()<<": "<<msg<<std::endl; |
} |
else throw std::runtime_error("Unrecognized directive: \""+list[0]+"\""); |
} |
|
LinkableObject::Word Assembler::elaborateDataDefinition(TokenList &list) { |
assert(!list.empty()); |
|
LinkableObject::Word rva=0; |
|
if(list[0]==".align") { |
if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\""); |
std::size_t align=4; |
if(list.size()>1) align=static_cast<std::size_t>(numericLiteral(list[1])); |
rva=_obj.addPadding(align); |
} |
else if(list[0]==".reserve") { |
if(list.size()<2) throw std::runtime_error("Unexpected end of statement"); |
else if(list.size()>2) throw std::runtime_error("Unexpected token: \""+list[2]+"\""); |
auto n=static_cast<std::size_t>(numericLiteral(list[1])); |
rva=_obj.addZeros(n); |
} |
else if(list[0]==".word") { |
if(list.size()<2) throw std::runtime_error("Unexpected end of statement"); |
for(std::size_t i=1;i<list.size();i++) { |
if(i%2!=0) { |
auto w=static_cast<LinkableObject::Word>(numericLiteral(list[i])); |
auto r=_obj.addWord(w); |
if(i==1) rva=r; |
} |
else { |
if(list[i]!=",") throw std::runtime_error("Comma expected"); |
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement"); |
} |
} |
} |
else if(list[0]==".byte") { |
if(list.size()<2) throw std::runtime_error("Unexpected end of statement"); |
for(std::size_t i=1;i<list.size();i++) { |
if(i%2!=0) { |
if(list[i].at(0)=='\"') { // string literal |
auto bytes=Utils::dequoteString(list[i]); |
auto r=_obj.addBytes(reinterpret_cast<const LinkableObject::Byte*> |
(bytes.c_str()),bytes.size()); |
if(i==1) rva=r; |
} |
else { |
auto n=numericLiteral(list[i]); |
|
if(n>255||n<-128) throw std::runtime_error("\""+list[i]+"\": out of range"); |
|
auto b=static_cast<LinkableObject::Byte>(n); |
auto r=_obj.addByte(b); |
if(i==1) rva=r; |
} |
} |
else { |
if(list[i]!=",") throw std::runtime_error("Comma expected"); |
if(i+1==list.size()) throw std::runtime_error("Unexpected end of statement"); |
} |
} |
} |
else throw std::runtime_error("Unrecognized statement: \""+list[0]+"\""); |
|
return rva; |
} |
|
LinkableObject::Word Assembler::elaborateInstruction(TokenList &list) { |
assert(!list.empty()); |
auto rva=_obj.addPadding(); |
if(list[0]=="add") encodeAdd(list); |
else if(list[0]=="and") encodeAnd(list); |
else if(list[0]=="call") encodeCall(list); |
else if(list[0].substr(0,4)=="cjmp") encodeCjmpxx(list); |
else if(list[0]=="divs") encodeDivs(list); |
else if(list[0]=="divu") encodeDivu(list); |
else if(list[0]=="hlt") encodeHlt(list); |
else if(list[0]=="jmp") encodeJmp(list); |
else if(list[0]=="iret") encodeIret(list); |
else if(list[0]=="lc") encodeLc(list); |
else if(list[0]=="lsb") encodeLsb(list); |
else if(list[0]=="lub") encodeLub(list); |
else if(list[0]=="lw") encodeLw(list); |
else if(list[0]=="mods") encodeMods(list); |
else if(list[0]=="modu") encodeModu(list); |
else if(list[0]=="mov") encodeMov(list); |
else if(list[0]=="mul") encodeMul(list); |
else if(list[0]=="nop") encodeNop(list); |
else if(list[0]=="not") encodeNot(list); |
else if(list[0]=="or") encodeOr(list); |
else if(list[0]=="ret") encodeRet(list); |
else if(list[0]=="sb") encodeSb(list); |
else if(list[0]=="sl") encodeSl(list); |
else if(list[0]=="srs") encodeSrs(list); |
else if(list[0]=="sru") encodeSru(list); |
else if(list[0]=="sub") encodeSub(list); |
else if(list[0]=="sw") encodeSw(list); |
else if(list[0]=="xor") encodeXor(list); |
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\""); |
return rva; |
} |
|
bool Assembler::validateIdentifier(const std::string &str) { |
/* |
* Valid identifier must satisfy the following requirements: |
* 1. Must not be empty |
* 2. The first character must be either alphabetic or an underscore |
* 3. Subsequent characters must be either alphanumeric or underscores |
*/ |
if(str.empty()) return false; |
for(std::size_t i=0;i<str.size();i++) { |
char ch=str[i]; |
if(i==0) { |
if(!std::isalpha(ch)&&ch!='_') return false; |
} |
else { |
if(!std::isalnum(ch)&&ch!='_') return false; |
} |
} |
return true; |
} |
|
Assembler::Integer Assembler::numericLiteral(const std::string &str) { |
std::size_t pos; |
Integer i; |
try { |
i=std::stoll(str,&pos,0); |
} |
catch(std::exception &) { |
throw std::runtime_error("Ill-formed numeric literal: \""+str+"\""); |
} |
if(pos<str.size()) throw std::runtime_error("Ill-formed numeric literal: \""+str+"\""); |
|
typedef std::make_signed<LinkableObject::Word>::type SignedWord; |
|
if(i>static_cast<Integer>(std::numeric_limits<LinkableObject::Word>::max())|| |
i<static_cast<Integer>(std::numeric_limits<SignedWord>::min())) |
throw std::runtime_error("\""+str+"\": out of range"); |
|
return i; |
} |
|
std::vector<Assembler::Operand> Assembler::getOperands(const TokenList &list) { |
std::vector<Operand> arglist; |
for(std::size_t i=1;i<list.size();i++) { |
if(i%2!=0) { |
Operand a; |
a.str=list[i]; |
|
if(!list[i].empty()&&list[i][0]=='r') { |
// Is argument a register? |
char *endptr; |
auto regstr=list[i].substr(1); |
auto reg=std::strtol(regstr.c_str(),&endptr,10); |
|
if(!*endptr&®>=0&®<=255) { |
a.type=Operand::Register; |
a.reg=static_cast<std::uint8_t>(reg); |
arglist.push_back(std::move(a)); |
continue; |
} |
} |
|
// Try alternative register names |
if(list[i]=="sp") { // stack pointer |
a.type=Operand::Register; |
a.reg=255; |
arglist.push_back(std::move(a)); |
} |
else if(list[i]=="rp") { // return pointer |
a.type=Operand::Register; |
a.reg=254; |
arglist.push_back(std::move(a)); |
} |
else if(list[i]=="irp") { // interrupt return pointer |
a.type=Operand::Register; |
a.reg=253; |
arglist.push_back(std::move(a)); |
} |
else if(list[i]=="cr") { // control register |
a.type=Operand::Register; |
a.reg=252; |
arglist.push_back(std::move(a)); |
} |
else if(list[i].size()==3&&list[i].substr(0,2)=="iv"&& |
std::isdigit(list[i][2])) // interrupt vector |
{ |
a.type=Operand::Register; |
a.reg=240+(list[i][2]-'0'); |
arglist.push_back(std::move(a)); |
} |
else if(validateIdentifier(list[i])) { |
// Is argument an identifier? |
a.type=Operand::Identifier; |
arglist.push_back(std::move(a)); |
} |
else { |
auto atpos=list[i].find_first_of('@'); |
if(atpos!=std::string::npos) { |
// Identifier with an offset? |
a.type=Operand::Identifier; |
a.str=list[i].substr(0,atpos); |
if(!validateIdentifier(a.str)) throw std::runtime_error("Ill-formed identifier"); |
a.i=numericLiteral(list[i].substr(atpos+1)); |
arglist.push_back(std::move(a)); |
} |
else { |
// Numeric literal? |
a.type=Operand::NumericLiteral; |
a.i=numericLiteral(list[i]); |
arglist.push_back(std::move(a)); |
} |
} |
} |
else { |
if(list[i]!=",") throw std::runtime_error("Comma expected"); |
if(i+1==list.size()) throw std::runtime_error("Unexpected end of line"); |
} |
} |
return arglist; |
} |
|
/* |
* Member functions to encode LXP32 instructions |
*/ |
|
void Assembler::encodeDstOperand(LinkableObject::Word &word,const Operand &arg) { |
if(arg.type!=Operand::Register) |
throw std::runtime_error("\""+arg.str+"\": must be a register"); |
word|=arg.reg<<16; |
} |
|
void Assembler::encodeRd1Operand(LinkableObject::Word &word,const Operand &arg) { |
if(arg.type==Operand::Register) { |
word|=0x02000000; |
word|=arg.reg<<8; |
} |
else if(arg.type==Operand::NumericLiteral) { |
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF)) |
throw std::runtime_error("\""+arg.str+"\": out of range"); |
auto b=static_cast<LinkableObject::Byte>(arg.i); |
word|=b<<8; |
} |
else throw std::runtime_error("\""+arg.str+"\": bad argument"); |
} |
|
void Assembler::encodeRd2Operand(LinkableObject::Word &word,const Operand &arg) { |
if(arg.type==Operand::Register) { |
word|=0x01000000; |
word|=arg.reg; |
} |
else if(arg.type==Operand::NumericLiteral) { |
if((arg.i<-128||arg.i>127)&&(arg.i<0xFFFFFF80||arg.i>0xFFFFFFFF)) |
throw std::runtime_error("\""+arg.str+"\": out of range"); |
auto b=static_cast<LinkableObject::Byte>(arg.i); |
word|=b; |
} |
else throw std::runtime_error("\""+arg.str+"\": bad argument"); |
} |
|
void Assembler::encodeAdd(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("add instruction requires 3 operands"); |
LinkableObject::Word w=0x40000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeAnd(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("and instruction requires 3 operands"); |
LinkableObject::Word w=0x60000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeCall(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=1) throw std::runtime_error("call instruction requires 1 operand"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
LinkableObject::Word w=0x86FE0000; |
encodeRd1Operand(w,args[0]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeCjmpxx(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("cjmpxx instruction requires 3 operands"); |
|
LinkableObject::Word w; |
bool reverse=false; |
/* |
* Note: cjmpul, cjmpule, cjmpsl and cjmpsle don't have distinct opcodes; |
* instead, they are aliases for respective "g" or "ge" instructions |
* with reversed operand order. |
*/ |
if(list[0]=="cjmpe") w=0xE0000000; |
else if(list[0]=="cjmpne") w=0xD0000000; |
else if(list[0]=="cjmpug"||list[0]=="cjmpul") w=0xC8000000; |
else if(list[0]=="cjmpuge"||list[0]=="cjmpule") w=0xE8000000; |
else if(list[0]=="cjmpsg"||list[0]=="cjmpsl") w=0xC4000000; |
else if(list[0]=="cjmpsge"||list[0]=="cjmpsle") w=0xE4000000; |
else throw std::runtime_error("Unrecognized instruction: \""+list[0]+"\""); |
|
if(list[0]=="cjmpul"||list[0]=="cjmpule"|| |
list[0]=="cjmpsl"||list[0]=="cjmpsle") reverse=true; |
|
encodeDstOperand(w,args[0]); |
|
if(!reverse) { |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
} |
else { |
encodeRd1Operand(w,args[2]); |
encodeRd2Operand(w,args[1]); |
} |
_obj.addWord(w); |
} |
|
void Assembler::encodeDivs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("divs instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&&args[2].i==0) { |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Division by zero"<<std::endl; |
} |
|
LinkableObject::Word w=0x54000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeDivu(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("divu instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&&args[2].i==0) { |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Division by zero"<<std::endl; |
} |
|
LinkableObject::Word w=0x50000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeHlt(const TokenList &list) { |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("hlt instruction doesn't take operands"); |
_obj.addWord(0x08000000); |
} |
|
void Assembler::encodeJmp(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=1) throw std::runtime_error("jmp instruction requires 1 operand"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
LinkableObject::Word w=0x82000000; |
encodeRd1Operand(w,args[0]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeIret(const TokenList &list) { |
// Note: "iret" is not a real instruction, but an alias for "jmp irp" |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("iret instruction doesn't take operands"); |
_obj.addWord(0x8200FD00); |
} |
|
void Assembler::encodeLc(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lc instruction requires 2 operands"); |
|
LinkableObject::Word w=0x04000000; |
encodeDstOperand(w,args[0]); |
_obj.addWord(w); |
|
if(args[1].type==Operand::Identifier) { |
auto symRva=_obj.addWord(static_cast<LinkableObject::Word>(args[1].i)); |
_obj.addReference(args[1].str,currentFileName(),line(),symRva); |
} |
else if(args[1].type==Operand::NumericLiteral) { |
_obj.addWord(static_cast<LinkableObject::Word>(args[1].i)); |
} |
else throw std::runtime_error("\""+args[1].str+"\": bad argument"); |
} |
|
void Assembler::encodeLsb(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lsb instruction requires 2 operands"); |
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register"); |
LinkableObject::Word w=0x2E000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeLub(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lub instruction requires 2 operands"); |
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register"); |
LinkableObject::Word w=0x2A000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeLw(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("lw instruction requires 2 operands"); |
if(args[1].type!=Operand::Register) throw std::runtime_error("\""+args[1].str+"\": must be a register"); |
LinkableObject::Word w=0x22000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeMods(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("mods instruction requires 3 operands"); |
LinkableObject::Word w=0x5C000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeModu(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("modu instruction requires 3 operands"); |
LinkableObject::Word w=0x58000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeMov(const TokenList &list) { |
// Note: "mov" is not a real instruction, but an alias for "add dst, src, 0" |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("mov instruction requires 2 operands"); |
LinkableObject::Word w=0x40000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeMul(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("mul instruction requires 3 operands"); |
LinkableObject::Word w=0x48000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeNop(const TokenList &list) { |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("nop instruction doesn't take operands"); |
_obj.addWord(0); |
} |
|
void Assembler::encodeNot(const TokenList &list) { |
// Note: "not" is not a real instruction, but an alias for "xor dst, src, -1" |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("not instruction requires 2 operands"); |
LinkableObject::Word w=0x680000FF; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeOr(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("or instruction requires 3 operands"); |
LinkableObject::Word w=0x64000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeRet(const TokenList &list) { |
// Note: "ret" is not a real instruction, but an alias for "jmp rp" |
auto args=getOperands(list); |
if(!args.empty()) throw std::runtime_error("ret instruction doesn't take operands"); |
_obj.addWord(0x8200FE00); |
} |
|
void Assembler::encodeSb(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("sb instruction requires 2 operands"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
if(args[1].type==Operand::NumericLiteral) { |
// If numeric literal value is between 128 and 255 (inclusive), convert |
// it to a signed byte to avoid exception in encodeRd2Operand() |
if(args[1].i>=128&&args[1].i<=255) args[1].i-=256; |
} |
LinkableObject::Word w=0x3A000000; |
encodeRd1Operand(w,args[0]); |
encodeRd2Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSl(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("sl instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&& |
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word)))) |
{ |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Bitwise shift result is undefined when " |
"the second operand is negative or greater than 31"<<std::endl; |
} |
|
LinkableObject::Word w=0x70000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSrs(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("srs instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&& |
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word)))) |
{ |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Bitwise shift result is undefined when " |
"the second operand is negative or greater than 31"<<std::endl; |
} |
|
LinkableObject::Word w=0x7C000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSru(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("sru instruction requires 3 operands"); |
if(args[2].type==Operand::NumericLiteral&& |
(args[2].i<0||args[2].i>=static_cast<Integer>(8*sizeof(LinkableObject::Word)))) |
{ |
std::cerr<<currentFileName()<<":"<<line()<<": "; |
std::cerr<<"Warning: Bitwise shift result is undefined when " |
"the second operand is negative or greater than 31"<<std::endl; |
} |
|
LinkableObject::Word w=0x78000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSub(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("sub instruction requires 3 operands"); |
LinkableObject::Word w=0x44000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeSw(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=2) throw std::runtime_error("sw instruction requires 2 operands"); |
if(args[0].type!=Operand::Register) throw std::runtime_error("\""+args[0].str+"\": must be a register"); |
LinkableObject::Word w=0x32000000; |
encodeRd1Operand(w,args[0]); |
encodeRd2Operand(w,args[1]); |
_obj.addWord(w); |
} |
|
void Assembler::encodeXor(const TokenList &list) { |
auto args=getOperands(list); |
if(args.size()!=3) throw std::runtime_error("xor instruction requires 3 operands"); |
LinkableObject::Word w=0x68000000; |
encodeDstOperand(w,args[0]); |
encodeRd1Operand(w,args[1]); |
encodeRd2Operand(w,args[2]); |
_obj.addWord(w); |
} |
/lxp32/tags/1.0/tools/src/lxp32asm/outputwriter.cpp
0,0 → 1,100
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the OutputWriter class |
* and its derived classes. |
*/ |
|
#include "outputwriter.h" |
#include "utils.h" |
|
#include <iostream> |
#include <iomanip> |
#include <stdexcept> |
#include <algorithm> |
#include <cassert> |
#include <cstdint> |
#include <cstdio> |
|
/* |
* OutputWriter members |
*/ |
|
void OutputWriter::pad(std::size_t size) { |
static char zeros[256]; // static objects are zero-initialized |
while(size>0) { |
auto n=std::min<std::size_t>(size,256); |
write(zeros,n); |
size-=n; |
} |
} |
|
/* |
* BinaryOutputWriter members |
*/ |
|
BinaryOutputWriter::BinaryOutputWriter(const std::string &filename): |
_filename(filename), |
_os(filename,std::ios_base::out|std::ios_base::binary) |
{ |
if(!_os) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
} |
|
void BinaryOutputWriter::write(const char *data,std::size_t n) { |
_os.write(data,n); |
} |
|
void BinaryOutputWriter::abort() { |
_os.close(); |
std::remove(_filename.c_str()); |
} |
|
/* |
* TextOutputWriter members |
*/ |
|
TextOutputWriter::TextOutputWriter(const std::string &filename,Format f): |
_filename(filename), |
_os(filename,std::ios_base::out), |
_fmt(f) |
{ |
if(!_os) throw std::runtime_error("Cannot open \""+filename+"\" for writing"); |
} |
|
TextOutputWriter::~TextOutputWriter() { |
if(!_buf.empty()) { |
assert(_buf.size()<4); |
pad(4-_buf.size()); |
} |
} |
|
void TextOutputWriter::write(const char *data,std::size_t n) { |
while(n>0) { |
assert(_buf.size()<4); |
auto count=std::min(4-_buf.size(),n); |
_buf.append(data,count); |
data+=count; |
n-=count; |
|
if(_buf.size()<4) continue; |
|
assert(_buf.size()==4); |
|
std::uint32_t word=(static_cast<unsigned char>(_buf[3])<<24)| |
(static_cast<unsigned char>(_buf[2])<<16)| |
(static_cast<unsigned char>(_buf[1])<<8)| |
static_cast<unsigned char>(_buf[0]); |
|
if(_fmt==Bin) _os<<Utils::bin(word)<<std::endl; |
else if(_fmt==Dec) _os<<word<<std::endl; |
else _os<<Utils::hex(word)<<std::endl; |
_buf.clear(); |
} |
} |
|
void TextOutputWriter::abort() { |
_os.close(); |
std::remove(_filename.c_str()); |
} |
/lxp32/tags/1.0/tools/src/lxp32asm/linkableobject.h
0,0 → 1,81
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the LinkableObject class which represents |
* compiled LXP32 binary code. |
*/ |
|
#ifndef LINKABLEOBJECT_H_INCLUDED |
#define LINKABLEOBJECT_H_INCLUDED |
|
#include <iostream> |
#include <vector> |
#include <map> |
#include <string> |
#include <cstdint> |
|
class LinkableObject { |
public: |
typedef unsigned char Byte; |
typedef std::uint32_t Word; |
|
enum SymbolType {Unknown,Local,External}; |
struct Reference { |
std::string source; |
int line; |
Word rva; |
}; |
struct SymbolData { |
SymbolType type=Unknown; |
Word rva; |
std::vector<Reference> refs; |
}; |
typedef std::map<std::string,SymbolData> SymbolTable; |
|
private: |
std::string _name; |
std::vector<Byte> _code; |
SymbolTable _symbols; |
Word _virtualAddress=0; |
|
public: |
std::string name() const; |
void setName(const std::string &str); |
|
Word virtualAddress() const; |
void setVirtualAddress(Word addr); |
|
Byte *code(); |
const Byte *code() const; |
std::size_t codeSize() const; |
|
Word addWord(Word w); |
Word addByte(Byte b); |
Word addBytes(const Byte *p,std::size_t n); |
Word addZeros(std::size_t n); |
|
Word addPadding(std::size_t size=sizeof(LinkableObject::Word)); |
|
Word getWord(Word rva) const; |
void replaceWord(Word rva,Word value); |
|
void addLocalSymbol(const std::string &name,Word rva); |
void addExternalSymbol(const std::string &name); |
void addReference(const std::string &symbolName,const std::string &source,int line,Word rva); |
|
SymbolData &symbol(const std::string &name); |
const SymbolData &symbol(const std::string &name) const; |
const SymbolTable &symbols() const; |
|
void serialize(const std::string &filename) const; |
void deserialize(const std::string &filename); |
|
private: |
void deserializeCode(std::istream &in); |
void deserializeSymbol(std::istream &in); |
static std::vector<std::string> tokenize(const std::string &str); |
}; |
|
#endif |
/lxp32/tags/1.0/tools/src/wigen/range.h
0,0 → 1,33
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Range class which represents |
* VHDL array ranges. |
*/ |
|
#ifndef RANGE_H_INCLUDED |
#define RANGE_H_INCLUDED |
|
#include <string> |
|
class Range { |
int _high; |
int _low; |
bool _valid; |
public: |
Range(); |
Range(int h,int l); |
|
void assign(int h,int l); |
void clear(); |
|
bool valid() const; |
int high() const; |
int low() const; |
int length() const; |
std::string toString() const; |
}; |
|
#endif |
/lxp32/tags/1.0/tools/src/wigen/main.cpp
0,0 → 1,128
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* Main translation unit for the WISHBONE interconnect generator. |
*/ |
|
#include "generator.h" |
|
#include <iostream> |
#include <string> |
#include <stdexcept> |
#include <cstring> |
#include <cstdlib> |
|
static void displayUsage(std::ostream &os,const char *program) { |
os<<std::endl; |
os<<"Usage:"<<std::endl; |
os<<" "<<program<<" [ option(s) ] <nm> <ns> <ma> <sa> <ps> [ <pg> ]"<<std::endl<<std::endl; |
os<<" <nm> Number of masters"<<std::endl; |
os<<" <ns> Number of slaves"<<std::endl; |
os<<" <ma> Master address width"<<std::endl; |
os<<" <sa> Slave address width"<<std::endl; |
os<<" <ps> Port size"<<std::endl; |
os<<" <pg> Port granularity, default: port size"<<std::endl; |
os<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -e <entity> Entity name, default: \"intercon\""<<std::endl; |
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -o <file> Output file name, default: \"<entity>.vhd\""<<std::endl; |
os<<" -p Generate pipelined arbiter"<<std::endl; |
os<<" -r Generate registered feedback signals"<<std::endl; |
os<<" -u Generate unsafe slave decoder"<<std::endl; |
} |
|
int main(int argc,char *argv[]) try { |
std::cout<<"WISHBONE interconnect generator"<<std::endl; |
std::cout<<"Copyright (c) 2016 by Alex I. Kuznetsov"<<std::endl; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
|
Generator gen; |
std::string outputFileName; |
int mainArg=0; |
|
for(int i=1;i<argc;i++) { |
if(argv[i][0]=='-') { |
if(!strcmp(argv[i],"-e")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
gen.setEntityName(argv[i]); |
} |
else if(!strcmp(argv[i],"-h")||!strcmp(argv[i],"--help")) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
outputFileName=argv[i]; |
} |
else if(!strcmp(argv[i],"-p")) { |
gen.setPipelinedArbiter(true); |
} |
else if(!strcmp(argv[i],"-r")) { |
gen.setRegisteredFeedback(true); |
} |
else if(!strcmp(argv[i],"-u")) { |
gen.setUnsafeDecoder(true); |
} |
else throw std::runtime_error(std::string("Unrecognized option: \"")+argv[i]+"\""); |
} |
else { |
if(mainArg>5) throw std::runtime_error("Too many arguments"); |
|
int value; |
|
try { |
value=std::stoi(argv[i],nullptr,0); |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid value"); |
} |
|
switch(mainArg) { |
case 0: |
gen.setMasters(value); |
break; |
case 1: |
gen.setSlaves(value); |
break; |
case 2: |
gen.setAddrWidth(value); |
break; |
case 3: |
gen.setSlaveAddrWidth(value); |
break; |
case 4: |
gen.setPortSize(value); |
break; |
case 5: |
gen.setPortGranularity(value); |
break; |
} |
mainArg++; |
} |
} |
|
if(mainArg<5) throw std::runtime_error("Too few arguments"); |
if(mainArg==5) gen.setPortGranularity(gen.portSize()); |
|
if(outputFileName.empty()) outputFileName=gen.entityName()+".vhd"; |
|
gen.generate(outputFileName); |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
/lxp32/tags/1.0/tools/src/wigen/generator.cpp
0,0 → 1,625
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Generator class. |
*/ |
|
#ifdef _MSC_VER |
#define _CRT_SECURE_NO_WARNINGS |
#endif |
|
#include "generator.h" |
|
#include <iostream> |
#include <fstream> |
#include <sstream> |
#include <stdexcept> |
#include <cctype> |
#include <ctime> |
|
Generator::Generator(): |
_masters(1), |
_slaves(1), |
_addrWidth(32), |
_portSize(32), |
_portGranularity(32), |
_entityName("intercon"), |
_pipelinedArbiter(false), |
_registeredFeedback(false), |
_unsafeDecoder(false) {} |
|
void Generator::setMasters(int i) { |
if(i<1) throw std::runtime_error("Invalid number of masters"); |
_masters=i; |
} |
|
void Generator::setSlaves(int i) { |
if(i<1) throw std::runtime_error("Invalid number of slaves"); |
_slaves=i; |
} |
|
void Generator::setAddrWidth(int i) { |
if(i<1) throw std::runtime_error("Invalid address width"); |
_addrWidth=i; |
} |
|
void Generator::setSlaveAddrWidth(int i) { |
if(i<1) throw std::runtime_error("Invalid slave address width"); |
_slaveAddrWidth=i; |
} |
|
void Generator::setPortSize(int i) { |
if(i!=8&&i!=16&&i!=32&&i!=64) |
throw std::runtime_error("Invalid port size: 8, 16, 32 or 64 expected"); |
_portSize=i; |
} |
|
void Generator::setPortGranularity(int i) { |
if(i!=8&&i!=16&&i!=32&&i!=64) |
throw std::runtime_error("Invalid port granularity: 8, 16, 32 or 64 expected"); |
_portGranularity=i; |
} |
|
void Generator::setEntityName(const std::string &str) try { |
if(str.empty()) throw std::exception(); |
// First character must be a letter |
if(!std::isalpha(str[0])) throw std::exception(); |
// Subsequent characters can be letters, digits or underscores |
for(std::size_t i=1;i<str.size();i++) |
if(!std::isalnum(str[i])&&str[i]!='_') throw std::exception(); |
|
_entityName=str; |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid entity name"); |
} |
|
void Generator::setPipelinedArbiter(bool b) { |
_pipelinedArbiter=b; |
} |
|
void Generator::setRegisteredFeedback(bool b) { |
_registeredFeedback=b; |
} |
|
void Generator::setUnsafeDecoder(bool b) { |
_unsafeDecoder=b; |
} |
|
int Generator::masters() const { |
return _masters; |
} |
|
int Generator::slaves() const { |
return _slaves; |
} |
|
int Generator::addrWidth() const { |
return _addrWidth; |
} |
|
int Generator::slaveAddrWidth() const { |
return _slaveAddrWidth; |
} |
|
int Generator::portSize() const { |
return _portSize; |
} |
|
int Generator::portGranularity() const { |
return _portGranularity; |
} |
|
std::string Generator::entityName() const { |
return _entityName; |
} |
|
bool Generator::pipelinedArbiter() const { |
return _pipelinedArbiter; |
} |
|
bool Generator::registeredFeedback() const { |
return _registeredFeedback; |
} |
|
bool Generator::unsafeDecoder() const { |
return _unsafeDecoder; |
} |
|
void Generator::generate(const std::string &filename) { |
prepare(); |
|
std::ofstream out(filename,std::ios_base::out); |
if(!out) throw std::runtime_error("Cannot open \""+filename+"\""); |
|
writeBanner(out); |
writePreamble(out); |
writeEntity(out); |
writeArchitecture(out); |
} |
|
/* |
* Private members |
*/ |
|
void Generator::prepare() { |
_fallbackSlave=false; |
if(slaves()<(1<<(addrWidth()-slaveAddrWidth()))&&!unsafeDecoder()) |
_fallbackSlave=true; |
|
_mastersRange.assign(masters()-1,0); |
if(_fallbackSlave) _slavesRange.assign(slaves(),0); |
else _slavesRange.assign(slaves()-1,0); |
|
int q=portSize()/portGranularity(); |
|
switch(q) { |
case 1: |
_addrRange.assign(addrWidth()-1,0); |
break; |
case 2: |
if(addrWidth()<=1) throw std::runtime_error("Invalid master address width"); |
_addrRange.assign(addrWidth()-1,1); |
_selRange.assign(1,0); |
break; |
case 4: |
if(addrWidth()<=2) throw std::runtime_error("Invalid master address width"); |
_addrRange.assign(addrWidth()-1,2); |
_selRange.assign(3,0); |
break; |
case 8: |
if(addrWidth()<=3) throw std::runtime_error("Invalid master address width"); |
_addrRange.assign(addrWidth()-1,3); |
_selRange.assign(7,0); |
break; |
default: |
throw std::runtime_error("Invalid port size/granularity combination"); |
} |
|
if(slaveAddrWidth()>addrWidth()) throw std::runtime_error("Invalid slave address width"); |
if(slaves()>(1<<(addrWidth()-slaveAddrWidth()))) throw std::runtime_error("Invalid slave address width"); |
if(slaveAddrWidth()<=_addrRange.low()) throw std::runtime_error("Invalid slave address width"); |
|
_slaveAddrRange.assign(slaveAddrWidth()-1,_addrRange.low()); |
|
if(slaves()>1) { |
_slaveDecoderRange.assign(_addrRange.high(),slaveAddrWidth()); |
if(unsafeDecoder()) { |
int requiredSize=0; |
while((1<<requiredSize)<slaves()) requiredSize++; |
_slaveDecoderRange.assign(_slaveDecoderRange.low()+requiredSize-1, |
_slaveDecoderRange.low()); |
} |
} |
_dataRange.assign(portSize()-1,0); |
} |
|
void Generator::writeBanner(std::ostream &os) { |
auto t=std::time(NULL); |
char szTime[256]; |
auto r=std::strftime(szTime,256,"%c",std::localtime(&t)); |
if(r==0) szTime[0]='\0'; |
|
os<<"---------------------------------------------------------------------"<<std::endl; |
os<<"-- Simple WISHBONE interconnect"<<std::endl; |
os<<"--"<<std::endl; |
os<<"-- Generated by wigen at "<<szTime<<std::endl; |
os<<"--"<<std::endl; |
os<<"-- Configuration:"<<std::endl; |
os<<"-- Number of masters: "<<masters()<<std::endl; |
os<<"-- Number of slaves: "<<slaves()<<std::endl; |
os<<"-- Master address width: "<<addrWidth()<<std::endl; |
os<<"-- Slave address width: "<<slaveAddrWidth()<<std::endl; |
os<<"-- Port size: "<<portSize()<<std::endl; |
os<<"-- Port granularity: "<<portGranularity()<<std::endl; |
os<<"-- Entity name: "<<entityName()<<std::endl; |
os<<"-- Pipelined arbiter: "<<(pipelinedArbiter()?"yes":"no")<<std::endl; |
os<<"-- Registered feedback: "<<(registeredFeedback()?"yes":"no")<<std::endl; |
os<<"-- Unsafe slave decoder: "<<(unsafeDecoder()?"yes":"no")<<std::endl; |
os<<"--"<<std::endl; |
os<<"-- Command line:"<<std::endl; |
os<<"-- wigen -e "<<entityName(); |
if(pipelinedArbiter()) os<<" -p"; |
if(registeredFeedback()) os<<" -r"; |
if(unsafeDecoder()) os<<" -u"; |
os<<" "<<masters()<<" "<<slaves()<<" "<<addrWidth()<<" "<<slaveAddrWidth(); |
os<<" "<<portSize()<<" "<<portGranularity()<<std::endl; |
os<<"---------------------------------------------------------------------"<<std::endl; |
os<<std::endl; |
} |
|
void Generator::writePreamble(std::ostream &os) { |
os<<"library ieee;"<<std::endl; |
os<<"use ieee.std_logic_1164.all;"<<std::endl; |
os<<std::endl; |
} |
|
void Generator::writeEntity(std::ostream &os) { |
os<<"entity "<<entityName()<<" is"<<std::endl; |
os<<"\tport("<<std::endl; |
os<<"\t\tclk_i: in std_logic;"<<std::endl; |
os<<"\t\trst_i: in std_logic;"<<std::endl; |
os<<std::endl; |
|
for(int i=0;i<masters();i++) { |
os<<"\t\ts"<<i<<"_cyc_i: in std_logic;"<<std::endl; |
os<<"\t\ts"<<i<<"_stb_i: in std_logic;"<<std::endl; |
os<<"\t\ts"<<i<<"_we_i: in std_logic;"<<std::endl; |
if(_selRange.valid()) |
os<<"\t\ts"<<i<<"_sel_i: in std_logic_vector("<<_selRange.toString()<<");"<<std::endl; |
if(registeredFeedback()) { |
os<<"\t\ts"<<i<<"_cti_i: in std_logic_vector(2 downto 0);"<<std::endl; |
os<<"\t\ts"<<i<<"_bte_i: in std_logic_vector(1 downto 0);"<<std::endl; |
} |
os<<"\t\ts"<<i<<"_ack_o: out std_logic;"<<std::endl; |
os<<"\t\ts"<<i<<"_adr_i: in std_logic_vector("<<_addrRange.toString()<<");"<<std::endl; |
os<<"\t\ts"<<i<<"_dat_i: in std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<"\t\ts"<<i<<"_dat_o: out std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<std::endl; |
} |
|
for(int i=0;i<slaves();i++) { |
os<<"\t\tm"<<i<<"_cyc_o: out std_logic;"<<std::endl; |
os<<"\t\tm"<<i<<"_stb_o: out std_logic;"<<std::endl; |
os<<"\t\tm"<<i<<"_we_o: out std_logic;"<<std::endl; |
if(_selRange.valid()) |
os<<"\t\tm"<<i<<"_sel_o: out std_logic_vector("<<_selRange.toString()<<");"<<std::endl; |
if(registeredFeedback()) { |
os<<"\t\tm"<<i<<"_cti_o: out std_logic_vector(2 downto 0);"<<std::endl; |
os<<"\t\tm"<<i<<"_bte_o: out std_logic_vector(1 downto 0);"<<std::endl; |
} |
os<<"\t\tm"<<i<<"_ack_i: in std_logic;"<<std::endl; |
os<<"\t\tm"<<i<<"_adr_o: out std_logic_vector("<<_slaveAddrRange.toString()<<");"<<std::endl; |
os<<"\t\tm"<<i<<"_dat_o: out std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<"\t\tm"<<i<<"_dat_i: in std_logic_vector("<<_dataRange.toString()<<")"; |
if(i!=slaves()-1) os<<";"<<std::endl<<std::endl; |
else os<<std::endl; |
} |
|
os<<"\t);"<<std::endl; |
os<<"end entity;"<<std::endl; |
os<<std::endl; |
} |
|
void Generator::writeArchitecture(std::ostream &os) { |
os<<"architecture rtl of "<<entityName()<<" is"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) { |
os<<"signal request: std_logic_vector("<< |
_mastersRange.toString()<<");"<<std::endl; |
os<<"signal grant_next: std_logic_vector("<< |
_mastersRange.toString()<<");"<<std::endl; |
os<<"signal grant: std_logic_vector("<< |
_mastersRange.toString()<<");"<<std::endl; |
|
if(!pipelinedArbiter()) { |
os<<"signal grant_reg: std_logic_vector("<< |
_mastersRange.toString()<<"):=(others=>\'0\');"<<std::endl; |
} |
|
os<<std::endl; |
} |
|
if(slaves()>1) { |
os<<"signal select_slave: std_logic_vector("<< |
_slavesRange.toString()<<");"<<std::endl; |
os<<std::endl; |
} |
|
os<<"signal cyc_mux: std_logic;"<<std::endl; |
os<<"signal stb_mux: std_logic;"<<std::endl; |
os<<"signal we_mux: std_logic;"<<std::endl; |
if(_selRange.valid()) |
os<<"signal sel_mux: std_logic_vector("<<_selRange.toString()<<");"<<std::endl; |
if(registeredFeedback()) { |
os<<"signal cti_mux: std_logic_vector(2 downto 0);"<<std::endl; |
os<<"signal bte_mux: std_logic_vector(1 downto 0);"<<std::endl; |
} |
os<<"signal adr_mux: std_logic_vector("<<_addrRange.toString()<<");"<<std::endl; |
os<<"signal wdata_mux: std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<std::endl; |
|
os<<"signal ack_mux: std_logic;"<<std::endl; |
os<<"signal rdata_mux: std_logic_vector("<<_dataRange.toString()<<");"<<std::endl; |
os<<std::endl; |
|
os<<"begin"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) writeArbiter(os); |
writeMasterMux(os); |
writeMasterDemux(os); |
writeSlaveMux(os); |
writeSlaveDemux(os); |
|
os<<"end architecture;"<<std::endl; |
} |
|
void Generator::writeArbiter(std::ostream &os) { |
os<<"-- ARBITER"<<std::endl; |
os<<"-- Selects the active master. Masters with lower port numbers"<<std::endl; |
os<<"-- have higher priority. Ongoing cycles are not interrupted."<<std::endl; |
os<<std::endl; |
|
os<<"request<="; |
for(int i=_mastersRange.high();i>=_mastersRange.low();i--) { |
os<<"s"<<i<<"_cyc_i"; |
if(i>0) os<<'&'; |
} |
os<<';'<<std::endl<<std::endl; |
|
os<<"grant_next<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<decodedLiteral(i,masters()); |
os<<" when request("<<i<<")=\'1\' else"<<std::endl; |
} |
os<<"\t(others=>\'0\');"<<std::endl; |
os<<std::endl; |
|
if(!pipelinedArbiter()) { |
os<<"grant<=grant_reg when (request and grant_reg)/="; |
os<<binaryLiteral(0,masters()); |
os<<" else grant_next;"<<std::endl; |
os<<std::endl; |
|
os<<"process (clk_i) is"<<std::endl; |
os<<"begin"<<std::endl; |
os<<"\tif rising_edge(clk_i) then"<<std::endl; |
os<<"\t\tif rst_i=\'1\' then"<<std::endl; |
os<<"\t\t\tgrant_reg<=(others=>\'0\');"<<std::endl; |
os<<"\t\telse"<<std::endl; |
os<<"\t\t\tgrant_reg<=grant;"<<std::endl; |
os<<"\t\tend if;"<<std::endl; |
os<<"\tend if;"<<std::endl; |
os<<"end process;"<<std::endl; |
os<<std::endl; |
} |
else { |
os<<"process (clk_i) is"<<std::endl; |
os<<"begin"<<std::endl; |
os<<"\tif rising_edge(clk_i) then"<<std::endl; |
os<<"\t\tif rst_i=\'1\' then"<<std::endl; |
os<<"\t\t\tgrant<=(others=>\'0\');"<<std::endl; |
os<<"\t\telsif (request and grant)="<<binaryLiteral(0,masters())<<" then"<<std::endl; |
os<<"\t\t\tgrant<=grant_next;"<<std::endl; |
os<<"\t\tend if;"<<std::endl; |
os<<"\tend if;"<<std::endl; |
os<<"end process;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeMasterMux(std::ostream &os) { |
os<<"-- MASTER->SLAVE MUX"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) { |
os<<"cyc_mux<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<"(s"<<i<<"_cyc_i and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<std::endl; |
|
os<<"stb_mux<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<"(s"<<i<<"_stb_i and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<std::endl; |
|
os<<"we_mux<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<'\t'; |
os<<"(s"<<i<<"_we_i and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<std::endl; |
|
if(_selRange.valid()) { |
os<<"sel_mux_gen: for i in sel_mux'range generate"<<std::endl; |
os<<"\tsel_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_sel_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
|
if(registeredFeedback()) { |
os<<"cti_mux_gen: for i in cti_mux'range generate"<<std::endl; |
os<<"\tcti_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_cti_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
|
os<<"bte_mux_gen: for i in bte_mux'range generate"<<std::endl; |
os<<"\tbte_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_bte_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
|
os<<"adr_mux_gen: for i in adr_mux'range generate"<<std::endl; |
os<<"\tadr_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_adr_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
|
os<<"wdata_mux_gen: for i in wdata_mux'range generate"<<std::endl; |
os<<"\twdata_mux(i)<="; |
for(int i=0;i<masters();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(s"<<i<<"_dat_i(i) and grant("<<i<<"))"; |
if(i<masters()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
else { // just one master |
os<<"cyc_mux<=s0_cyc_i;"<<std::endl; |
os<<"stb_mux<=s0_stb_i;"<<std::endl; |
os<<"we_mux<=s0_we_i;"<<std::endl; |
if(_selRange.valid()) os<<"sel_mux<=s0_sel_i;"<<std::endl; |
if(registeredFeedback()) { |
os<<"cti_mux<=s0_cti_i;"<<std::endl; |
os<<"bte_mux<=s0_bte_i;"<<std::endl; |
} |
os<<"adr_mux<=s0_adr_i;"<<std::endl; |
os<<"wdata_mux<=s0_dat_i;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeMasterDemux(std::ostream &os) { |
os<<"-- MASTER->SLAVE DEMUX"<<std::endl; |
os<<std::endl; |
|
if(slaves()>1) { |
os<<"select_slave<="; |
for(int i=0;i<slaves();i++) { |
if(i>0) os<<'\t'; |
os<<decodedLiteral(i,_slavesRange.length()); |
os<<" when adr_mux("<<_slaveDecoderRange.toString()<<")="; |
os<<binaryLiteral(i,_slaveDecoderRange.length())<<" else"<<std::endl; |
} |
if(_fallbackSlave) { |
os<<'\t'<<decodedLiteral(slaves(),_slavesRange.length())<< |
"; -- fallback slave"<<std::endl; |
} |
else os<<"\t(others=>'-');"<<std::endl; |
os<<std::endl; |
|
for(int i=0;i<slaves();i++) { |
os<<'m'<<i<<"_cyc_o<=cyc_mux and select_slave("<<i<<");"<<std::endl; |
os<<'m'<<i<<"_stb_o<=stb_mux and select_slave("<<i<<");"<<std::endl; |
os<<'m'<<i<<"_we_o<=we_mux;"<<std::endl; |
if(_selRange.valid()) os<<'m'<<i<<"_sel_o<=sel_mux;"<<std::endl; |
if(registeredFeedback()) { |
os<<'m'<<i<<"_cti_o<=cti_mux;"<<std::endl; |
os<<'m'<<i<<"_bte_o<=bte_mux;"<<std::endl; |
} |
os<<'m'<<i<<"_adr_o<=adr_mux(m"<<i<<"_adr_o'range);"<<std::endl; |
os<<'m'<<i<<"_dat_o<=wdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
else { // just one slave |
os<<"m0_cyc_o<=cyc_mux;"<<std::endl; |
os<<"m0_stb_o<=stb_mux;"<<std::endl; |
os<<"m0_we_o<=we_mux;"<<std::endl; |
if(_selRange.valid()) os<<"m0_sel_o<=sel_mux;"<<std::endl; |
if(registeredFeedback()) { |
os<<"m0_cti_o<=cti_mux;"<<std::endl; |
os<<"m0_bte_o<=bte_mux;"<<std::endl; |
} |
os<<"m0_adr_o<=adr_mux(m0_adr_o'range);"<<std::endl; |
os<<"m0_dat_o<=wdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeSlaveMux(std::ostream &os) { |
os<<"-- SLAVE->MASTER MUX"<<std::endl; |
os<<std::endl; |
|
if(slaves()>1) { |
os<<"ack_mux<="; |
for(int i=0;i<slaves();i++) { |
if(i>0) os<<'\t'; |
os<<"(m"<<i<<"_ack_i and select_slave("<<i<<"))"; |
if(i<slaves()-1||_fallbackSlave) os<<" or"<<std::endl; |
else os<<';'<<std::endl; |
} |
if(_fallbackSlave) { |
os<<"\t(cyc_mux and stb_mux and select_slave("<<slaves()<< |
")); -- fallback slave"<<std::endl; |
} |
os<<std::endl; |
|
os<<"rdata_mux_gen: for i in rdata_mux'range generate"<<std::endl; |
os<<"\trdata_mux(i)<="; |
for(int i=0;i<slaves();i++) { |
if(i>0) os<<"\t\t"; |
os<<"(m"<<i<<"_dat_i(i) and select_slave("<<i<<"))"; |
if(i<slaves()-1) os<<" or"<<std::endl; |
else os<<";"<<std::endl; |
} |
os<<"end generate;"<<std::endl; |
os<<std::endl; |
} |
else { // just one slave |
os<<"ack_mux<=m0_ack_i;"<<std::endl; |
os<<"rdata_mux<=m0_dat_i;"<<std::endl; |
os<<std::endl; |
} |
} |
|
void Generator::writeSlaveDemux(std::ostream &os) { |
os<<"-- SLAVE->MASTER DEMUX"<<std::endl; |
os<<std::endl; |
|
if(masters()>1) { |
for(int i=0;i<masters();i++) { |
os<<'s'<<i<<"_ack_o<=ack_mux and grant("<<i<<");"<<std::endl; |
os<<'s'<<i<<"_dat_o<=rdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
else { // just one master |
os<<"s0_ack_o<=ack_mux;"<<std::endl; |
os<<"s0_dat_o<=rdata_mux;"<<std::endl; |
os<<std::endl; |
} |
} |
|
std::string Generator::binaryLiteral(int value,int n) { |
std::ostringstream oss; |
oss.put('\"'); |
for(int i=n-1;i>=0;i--) { |
if(value>=static_cast<int>(sizeof(int)*8)) oss.put('0'); |
else if((value>>i)&1) oss.put('1'); |
else oss.put('0'); |
} |
oss.put('\"'); |
return oss.str(); |
} |
|
std::string Generator::decodedLiteral(int value,int n) { |
std::ostringstream oss; |
oss.put('\"'); |
for(int i=n-1;i>=0;i--) { |
if(value==i) oss.put('1'); |
else oss.put('0'); |
} |
oss.put('\"'); |
return oss.str(); |
} |
/lxp32/tags/1.0/tools/src/wigen/generator.h
0,0 → 1,86
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Generator class which generates |
* WISHBONE interconnect VHDL description based on provided |
* parameters. |
*/ |
|
#ifndef GENERATOR_H_INCLUDED |
#define GENERATOR_H_INCLUDED |
|
#include "range.h" |
|
#include <iostream> |
#include <string> |
|
class Generator { |
int _masters; |
int _slaves; |
int _addrWidth; |
int _slaveAddrWidth; |
int _portSize; |
int _portGranularity; |
|
std::string _entityName; |
bool _pipelinedArbiter; |
bool _registeredFeedback; |
bool _unsafeDecoder; |
|
Range _mastersRange; |
Range _slavesRange; |
Range _addrRange; |
Range _slaveAddrRange; |
Range _slaveDecoderRange; |
Range _dataRange; |
Range _selRange; |
|
bool _fallbackSlave; |
|
public: |
Generator(); |
|
void setMasters(int i); |
void setSlaves(int i); |
void setAddrWidth(int i); |
void setSlaveAddrWidth(int i); |
void setPortSize(int i); |
void setPortGranularity(int i); |
void setEntityName(const std::string &str); |
void setPipelinedArbiter(bool b); |
void setRegisteredFeedback(bool b); |
void setUnsafeDecoder(bool b); |
|
int masters() const; |
int slaves() const; |
int addrWidth() const; |
int slaveAddrWidth() const; |
int portSize() const; |
int portGranularity() const; |
std::string entityName() const; |
bool pipelinedArbiter() const; |
bool registeredFeedback() const; |
bool unsafeDecoder() const; |
|
void generate(const std::string &filename); |
|
private: |
void prepare(); |
void writeBanner(std::ostream &os); |
void writePreamble(std::ostream &os); |
void writeEntity(std::ostream &os); |
void writeArchitecture(std::ostream &os); |
|
void writeArbiter(std::ostream &os); |
void writeMasterMux(std::ostream &os); |
void writeMasterDemux(std::ostream &os); |
void writeSlaveMux(std::ostream &os); |
void writeSlaveDemux(std::ostream &os); |
|
static std::string binaryLiteral(int value,int n); |
static std::string decodedLiteral(int value,int n); |
}; |
|
#endif |
/lxp32/tags/1.0/tools/src/wigen/range.cpp
0,0 → 1,52
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Range class. |
*/ |
|
#include "range.h" |
|
#include <stdexcept> |
|
Range::Range(): _valid(false) {} |
|
Range::Range(int h,int l): _high(h),_low(l),_valid(true) { |
if(l>h) throw std::runtime_error("Invalid range"); |
} |
|
void Range::assign(int h,int l) { |
if(l>h) throw std::runtime_error("Invalid range"); |
_high=h; |
_low=l; |
_valid=true; |
} |
|
void Range::clear() { |
_valid=false; |
} |
|
bool Range::valid() const { |
return _valid; |
} |
|
int Range::high() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return _high; |
} |
|
int Range::low() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return _low; |
} |
|
int Range::length() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return _high-_low+1; |
} |
|
std::string Range::toString() const { |
if(!_valid) throw std::runtime_error("Invalid range"); |
return std::to_string(_high)+" downto "+std::to_string(_low); |
} |
/lxp32/tags/1.0/tools/src/wigen/CMakeLists.txt
0,0 → 1,7
cmake_minimum_required(VERSION 3.3.0) |
|
add_executable(wigen generator.cpp main.cpp range.cpp) |
|
# Install |
|
install(TARGETS wigen DESTINATION .) |
/lxp32/tags/1.0/tools/src/CMakeLists.txt
0,0 → 1,50
cmake_minimum_required(VERSION 3.3.0) |
project(lxp32tools) |
|
# Examine environment |
|
if(CMAKE_C_COMPILER_ID STREQUAL GNU OR CMAKE_C_COMPILER_ID STREQUAL Clang) |
set(GNU_SYNTAX TRUE) |
endif() |
|
# Set default install prefix if not manually set by the user |
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) |
set(CMAKE_INSTALL_PREFIX "${PROJECT_SOURCE_DIR}/../bin" CACHE PATH "Install prefix" FORCE) |
endif() |
|
message("Install prefix: ${CMAKE_INSTALL_PREFIX}") |
|
# Enable C++11 |
|
set(CMAKE_CXX_STANDARD 11) |
|
# Set up warning level for GCC/Clang |
|
if(GNU_SYNTAX) |
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pedantic -Wall -Wextra") |
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra") |
endif() |
|
# On Windows, link runtime statically |
|
if(WIN32) |
if(GNU_SYNTAX) |
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static") |
elseif(MSVC) |
foreach(flag_var |
CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE |
CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO |
CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE |
CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO |
) |
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") |
endforeach(flag_var) |
endif() |
endif() |
|
# Build targets |
|
add_subdirectory(lxp32asm) |
add_subdirectory(lxp32dump) |
add_subdirectory(wigen) |
/lxp32/tags/1.0/tools/src/lxp32dump/disassembler.h
0,0 → 1,93
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module defines the Disassembler class which disassembles |
* LXP32 executable code. |
*/ |
|
#ifndef DISASSEMBLER_H_INCLUDED |
#define DISASSEMBLER_H_INCLUDED |
|
#include <iostream> |
#include <type_traits> |
#include <cstdint> |
|
class Disassembler { |
public: |
enum Format {Bin,Textio,Dec,Hex}; |
typedef std::uint32_t Word; |
private: |
class Operand { |
public: |
enum Type {Register,Direct}; |
private: |
Type _type; |
int _value; |
public: |
Operand(Type t,int value); |
|
Type type() const; |
int value() const; |
std::string str() const; |
}; |
|
std::istream &_is; |
std::ostream &_os; |
Format _fmt; |
int _lineNumber; |
Word _pos; |
public: |
Disassembler(std::istream &is,std::ostream &os); |
void setFormat(Format fmt); |
void setBase(Word base); |
void dump(); |
|
template <typename T> static std::string hex(const T &w) { |
static_assert(std::is_integral<T>::value,"Argument must be of integral type"); |
const char *hexstr="0123456789ABCDEF"; |
std::string res; |
|
res.reserve(sizeof(T)*2); |
|
for(int i=sizeof(T)*8-4;i>=0;i-=4) { |
res.push_back(hexstr[(w>>i)&0x0F]); |
} |
return res; |
} |
private: |
bool getWord(Word &w); |
static Operand decodeRd1Operand(Word w); |
static Operand decodeRd2Operand(Word w); |
static Operand decodeDstOperand(Word w); |
static std::string decodeSimpleInstruction(const std::string &op,Word w); |
|
std::string decodeAdd(Word w); |
std::string decodeAnd(Word w); |
std::string decodeCall(Word w); |
std::string decodeCjmpxx(Word w); |
std::string decodeDivs(Word w); |
std::string decodeDivu(Word w); |
std::string decodeHlt(Word w); |
std::string decodeJmp(Word w); |
std::string decodeLc(Word w,bool &valid,Word &operand); |
std::string decodeLsb(Word w); |
std::string decodeLub(Word w); |
std::string decodeLw(Word w); |
std::string decodeMods(Word w); |
std::string decodeModu(Word w); |
std::string decodeMul(Word w); |
std::string decodeNop(Word w); |
std::string decodeOr(Word w); |
std::string decodeSb(Word w); |
std::string decodeSl(Word w); |
std::string decodeSrs(Word w); |
std::string decodeSru(Word w); |
std::string decodeSub(Word w); |
std::string decodeSw(Word w); |
std::string decodeXor(Word w); |
std::string decodeWord(Word w); |
}; |
|
#endif |
/lxp32/tags/1.0/tools/src/lxp32dump/main.cpp
0,0 → 1,193
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* Main translation unit for the LXP32 disassembler. |
*/ |
|
#ifdef _MSC_VER |
#define _CRT_SECURE_NO_WARNINGS |
#endif |
|
#include "disassembler.h" |
|
#include <iostream> |
#include <fstream> |
#include <string> |
#include <stdexcept> |
#include <cstring> |
#include <cstdlib> |
#include <cstdio> |
#include <ctime> |
|
static void displayUsage(std::ostream &os,const char *program) { |
os<<std::endl; |
os<<"Usage:"<<std::endl; |
os<<" "<<program<<" [ option(s) | input file ]"<<std::endl<<std::endl; |
|
os<<"Options:"<<std::endl; |
os<<" -b <addr> Base address (for comments only)"<<std::endl; |
os<<" -f <fmt> Input format (bin, textio, dec, hex), default: autodetect"<<std::endl; |
os<<" -h, --help Display a short help message"<<std::endl; |
os<<" -o <file> Output file name, default: standard output"<<std::endl; |
os<<" -- Do not interpret subsequent arguments as options"<<std::endl; |
} |
|
static Disassembler::Format detectInputFormat(std::istream &in) { |
static const std::size_t Size=256; |
static const char *textio="01\r\n \t"; |
static const char *dec="0123456789\r\n \t"; |
static const char *hex="0123456789ABCDEFabcdef\r\n \t"; |
|
char buf[Size]; |
in.read(buf,Size); |
auto s=static_cast<std::size_t>(in.gcount()); |
in.clear(); |
in.seekg(0); |
|
Disassembler::Format fmt=Disassembler::Textio; |
|
for(std::size_t i=0;i<s;i++) { |
if(fmt==Disassembler::Textio&&!strchr(textio,buf[i])) fmt=Disassembler::Dec; |
if(fmt==Disassembler::Dec&&!strchr(dec,buf[i])) fmt=Disassembler::Hex; |
if(fmt==Disassembler::Hex&&!strchr(hex,buf[i])) { |
fmt=Disassembler::Bin; |
break; |
} |
} |
|
return fmt; |
} |
|
int main(int argc,char *argv[]) try { |
std::string inputFileName,outputFileName; |
|
std::cerr<<"LXP32 Platform Disassembler"<<std::endl; |
std::cerr<<"Copyright (c) 2016 by Alex I. Kuznetsov"<<std::endl; |
|
Disassembler::Format fmt=Disassembler::Bin; |
bool noMoreOptions=false; |
bool formatSpecified=false; |
Disassembler::Word base=0; |
|
if(argc<=1) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
|
for(int i=1;i<argc;i++) { |
if(argv[i][0]!='-'||noMoreOptions) { |
if(inputFileName.empty()) inputFileName=argv[i]; |
else throw std::runtime_error("Only one input file name can be specified"); |
} |
else if(!strcmp(argv[i],"--")) noMoreOptions=true; |
else if(!strcmp(argv[i],"-b")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
try { |
base=std::stoul(argv[i],nullptr,0); |
if(base%4!=0) throw std::exception(); |
} |
catch(std::exception &) { |
throw std::runtime_error("Invalid base address"); |
} |
} |
else if(!strcmp(argv[i],"-f")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
if(!strcmp(argv[i],"bin")) fmt=Disassembler::Bin; |
else if(!strcmp(argv[i],"textio")) fmt=Disassembler::Textio; |
else if(!strcmp(argv[i],"dec")) fmt=Disassembler::Dec; |
else if(!strcmp(argv[i],"hex")) fmt=Disassembler::Hex; |
else throw std::runtime_error("Unrecognized input format"); |
formatSpecified=true; |
} |
else if(!strcmp(argv[i],"-h")||!strcmp(argv[i],"--help")) { |
displayUsage(std::cout,argv[0]); |
return 0; |
} |
else if(!strcmp(argv[i],"-o")) { |
if(++i==argc) { |
displayUsage(std::cerr,argv[0]); |
return EXIT_FAILURE; |
} |
outputFileName=argv[i]; |
} |
else throw std::runtime_error(std::string("Unrecognized option: \"")+argv[i]+"\""); |
} |
|
if(!formatSpecified) { // auto-detect input file format |
std::ifstream in(inputFileName,std::ios_base::in|std::ios_base::binary); |
fmt=detectInputFormat(in); |
} |
|
std::ifstream in; |
|
if(fmt==Disassembler::Bin) in.open(inputFileName,std::ios_base::in|std::ios_base::binary); |
else in.open(inputFileName,std::ios_base::in); |
if(!in) throw std::runtime_error("Cannot open \""+inputFileName+"\""); |
|
std::ofstream out; |
std::ostream *os=&std::cout; |
if(!outputFileName.empty()) { |
out.open(outputFileName,std::ios_base::out); |
if(!out) throw std::runtime_error("Cannot open \""+outputFileName+"\""); |
os=&out; |
} |
|
auto t=std::time(NULL); |
char szTime[256]; |
auto r=std::strftime(szTime,256,"%c",std::localtime(&t)); |
if(r==0) szTime[0]='\0'; |
|
*os<<"/*"<<std::endl; |
*os<<" * Input file: "<<inputFileName<<std::endl; |
*os<<" * Input format: "; |
|
switch(fmt) { |
case Disassembler::Bin: |
*os<<"bin"; |
break; |
case Disassembler::Textio: |
*os<<"textio"; |
break; |
case Disassembler::Dec: |
*os<<"dec"; |
break; |
case Disassembler::Hex: |
*os<<"hex"; |
break; |
default: |
break; |
} |
|
if(!formatSpecified) *os<<" (autodetected)"; |
*os<<std::endl; |
*os<<" * Base address: 0x"<<Disassembler::hex(base)<<std::endl; |
*os<<" * Disassembled by lxp32dump at "<<szTime<<std::endl; |
*os<<" */"<<std::endl<<std::endl; |
|
Disassembler disasm(in,*os); |
disasm.setFormat(fmt); |
disasm.setBase(base); |
|
try { |
disasm.dump(); |
} |
catch(std::exception &) { |
if(!outputFileName.empty()) { |
out.close(); |
std::remove(outputFileName.c_str()); |
} |
throw; |
} |
} |
catch(std::exception &ex) { |
std::cerr<<"Error: "<<ex.what()<<std::endl; |
return EXIT_FAILURE; |
} |
/lxp32/tags/1.0/tools/src/lxp32dump/CMakeLists.txt
0,0 → 1,7
cmake_minimum_required(VERSION 3.3.0) |
|
add_executable(lxp32dump disassembler.cpp main.cpp) |
|
# Install |
|
install(TARGETS lxp32dump DESTINATION .) |
/lxp32/tags/1.0/tools/src/lxp32dump/disassembler.cpp
0,0 → 1,456
/* |
* Copyright (c) 2016 by Alex I. Kuznetsov. |
* |
* Part of the LXP32 CPU IP core. |
* |
* This module implements members of the Disassembler class. |
*/ |
|
#include "disassembler.h" |
|
#include <sstream> |
#include <stdexcept> |
|
/* |
* Disassembler::Operand class members |
*/ |
|
Disassembler::Operand::Operand(Type t,int value): |
_type(t),_value(value) {} |
|
Disassembler::Operand::Type Disassembler::Operand::type() const { |
return _type; |
} |
|
int Disassembler::Operand::value() const { |
return _value; |
} |
|
std::string Disassembler::Operand::str() const { |
if(_type==Register) { |
if(_value>=240&&_value<=247) return "iv"+std::to_string(_value-240); |
else if(_value==252) return "cr"; |
else if(_value==253) return "irp"; |
else if(_value==254) return "rp"; |
else if(_value==255) return "sp"; |
else return "r"+std::to_string(_value); |
} |
else return std::to_string(_value); |
} |
|
/* |
* Disassembler class members |
*/ |
|
Disassembler::Disassembler(std::istream &is,std::ostream &os): |
_is(is),_os(os),_fmt(Bin),_lineNumber(0),_pos(0) {} |
|
void Disassembler::setFormat(Format fmt) { |
_fmt=fmt; |
} |
|
void Disassembler::setBase(Word base) { |
_pos=base; |
} |
|
void Disassembler::dump() { |
Word word; |
|
for(;;) { |
auto offset=_pos; |
if(!getWord(word)) break; |
auto opcode=word>>26; |
|
std::string instruction; |
|
bool lcValid=false; |
Word lcOperand; |
|
switch(opcode) { |
case 0x10: |
instruction=decodeAdd(word); |
break; |
case 0x18: |
instruction=decodeAnd(word); |
break; |
case 0x21: |
instruction=decodeCall(word); |
break; |
case 0x15: |
instruction=decodeDivs(word); |
break; |
case 0x14: |
instruction=decodeDivu(word); |
break; |
case 0x02: |
instruction=decodeHlt(word); |
break; |
case 0x20: |
instruction=decodeJmp(word); |
break; |
case 0x01: |
instruction=decodeLc(word,lcValid,lcOperand); |
break; |
case 0x0B: |
instruction=decodeLsb(word); |
break; |
case 0x0A: |
instruction=decodeLub(word); |
break; |
case 0x08: |
instruction=decodeLw(word); |
break; |
case 0x17: |
instruction=decodeMods(word); |
break; |
case 0x16: |
instruction=decodeModu(word); |
break; |
case 0x12: |
instruction=decodeMul(word); |
break; |
case 0x00: |
instruction=decodeNop(word); |
break; |
case 0x19: |
instruction=decodeOr(word); |
break; |
case 0x0E: |
instruction=decodeSb(word); |
break; |
case 0x1C: |
instruction=decodeSl(word); |
break; |
case 0x1F: |
instruction=decodeSrs(word); |
break; |
case 0x1E: |
instruction=decodeSru(word); |
break; |
case 0x11: |
instruction=decodeSub(word); |
break; |
case 0x0C: |
instruction=decodeSw(word); |
break; |
case 0x1A: |
instruction=decodeXor(word); |
break; |
default: |
if((opcode>>4)==0x03) instruction=decodeCjmpxx(word); |
else instruction=decodeWord(word); |
} |
|
auto size=instruction.size(); |
std::size_t padding=0; |
if(size<32) padding=32-size; |
|
_os<<'\t'<<instruction<<std::string(padding,' ')<<"// "; |
_os<<hex(offset)<<": "<<hex(word); |
if(lcValid) _os<<' '<<hex(lcOperand); |
_os<<std::endl; |
} |
} |
|
bool Disassembler::getWord(Word &w) { |
if(_fmt==Bin) { |
char buf[sizeof(Word)] {}; // zero-initialize |
_is.read(buf,sizeof(Word)); |
|
auto n=static_cast<std::size_t>(_is.gcount()); |
if(n==0) return false; |
if(n<sizeof(Word)) std::cerr<<"Warning: last word is truncated"<<std::endl; |
|
w=(static_cast<unsigned char>(buf[3])<<24)|(static_cast<unsigned char>(buf[2])<<16)| |
(static_cast<unsigned char>(buf[1])<<8)|static_cast<unsigned char>(buf[0]); |
} |
else { |
try { |
std::string line; |
if(!std::getline(_is,line)) return false; |
_lineNumber++; |
|
if(_fmt==Textio) w=std::stoul(line,nullptr,2); |
else if(_fmt==Dec) w=std::stoul(line,nullptr,10); |
else if(_fmt==Hex) w=std::stoul(line,nullptr,16); |
else return false; |
} |
catch(std::exception &) { |
throw std::runtime_error("Bad literal at line "+std::to_string(_lineNumber)); |
} |
} |
_pos+=sizeof(Word); |
return true; |
} |
|
Disassembler::Operand Disassembler::decodeRd1Operand(Word w) { |
int value=(w>>8)&0xFF; |
if(w&0x02000000) return Operand(Operand::Register,value); |
else { |
if(value>127) value-=256; |
return Operand(Operand::Direct,value); |
} |
} |
|
Disassembler::Operand Disassembler::decodeRd2Operand(Word w) { |
int value=w&0xFF; |
if(w&0x01000000) return Operand(Operand::Register,value); |
else { |
if(value>127) value-=256; |
return Operand(Operand::Direct,value); |
} |
} |
|
Disassembler::Operand Disassembler::decodeDstOperand(Word w) { |
int value=(w>>16)&0xFF; |
return Operand(Operand::Register,value); |
} |
|
std::string Disassembler::decodeSimpleInstruction(const std::string &op,Word w) { |
std::ostringstream oss; |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
oss<<op<<' '<<dst.str()<<", "<<rd1.str()<<", "<<rd2.str(); |
return oss.str(); |
} |
|
std::string Disassembler::decodeAdd(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd2.type()==Operand::Direct&&rd2.value()==0) |
oss<<"mov "<<dst.str()<<", "<<rd1.str(); |
else |
oss<<"add "<<dst.str()<<", "<<rd1.str()<<", "<<rd2.str(); |
|
return oss.str(); |
} |
|
std::string Disassembler::decodeAnd(Word w) { |
return decodeSimpleInstruction("and",w); |
} |
|
std::string Disassembler::decodeCall(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0xFE) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "call "+rd1.str(); |
} |
|
std::string Disassembler::decodeCjmpxx(Word w) { |
auto jumpType=(w>>26)&0x0F; |
std::string op; |
|
switch(jumpType) { |
case 0x8: |
op="cjmpe"; |
break; |
case 0x4: |
op="cjmpne"; |
break; |
case 0x2: |
op="cjmpug"; |
break; |
case 0xA: |
op="cjmpuge"; |
break; |
case 0x1: |
op="cjmpsg"; |
break; |
case 0x9: |
op="cjmpsge"; |
break; |
default: |
return decodeWord(w); |
} |
|
return decodeSimpleInstruction(op,w); |
} |
|
std::string Disassembler::decodeDivs(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("divs",w); |
} |
|
std::string Disassembler::decodeDivu(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("divu",w); |
} |
|
std::string Disassembler::decodeHlt(Word w) { |
if(w!=0x08000000) return decodeWord(w); |
return "hlt"; |
} |
|
std::string Disassembler::decodeJmp(Word w) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
if(rd1.value()==253) return "iret"; |
if(rd1.value()==254) return "ret"; |
return "jmp "+rd1.str(); |
} |
|
std::string Disassembler::decodeLc(Word w,bool &valid,Word &operand) { |
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
valid=false; |
|
if(rd1.type()!=Operand::Direct||rd1.value()!=0) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
bool b=getWord(operand); |
if(!b) return decodeWord(w); |
|
valid=true; |
return "lc "+dst.str()+", 0x"+hex(operand); |
} |
|
std::string Disassembler::decodeLsb(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "lsb "+dst.str()+", "+rd1.str(); |
} |
|
std::string Disassembler::decodeLub(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "lub "+dst.str()+", "+rd1.str(); |
} |
|
std::string Disassembler::decodeLw(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd1.type()!=Operand::Register) return decodeWord(w); |
if(rd2.type()!=Operand::Direct||rd2.value()!=0) return decodeWord(w); |
|
return "lw "+dst.str()+", "+rd1.str(); |
} |
|
std::string Disassembler::decodeMods(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("mods",w); |
} |
|
std::string Disassembler::decodeModu(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&rd2.value()==0) return decodeWord(w); |
return decodeSimpleInstruction("modu",w); |
} |
|
std::string Disassembler::decodeMul(Word w) { |
return decodeSimpleInstruction("mul",w); |
} |
|
std::string Disassembler::decodeNop(Word w) { |
if(w!=0) return decodeWord(w); |
return "nop"; |
} |
|
std::string Disassembler::decodeOr(Word w) { |
return decodeSimpleInstruction("or",w); |
} |
|
std::string Disassembler::decodeSb(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
|
return "sb "+rd1.str()+", "+rd2.str(); |
} |
|
std::string Disassembler::decodeSl(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&(rd2.value()<0||rd2.value()>31)) return decodeWord(w); |
return decodeSimpleInstruction("sl",w); |
} |
|
std::string Disassembler::decodeSrs(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&(rd2.value()<0||rd2.value()>31)) return decodeWord(w); |
return decodeSimpleInstruction("srs",w); |
} |
|
std::string Disassembler::decodeSru(Word w) { |
auto rd2=decodeRd2Operand(w); |
if(rd2.type()==Operand::Direct&&(rd2.value()<0||rd2.value()>31)) return decodeWord(w); |
return decodeSimpleInstruction("sru",w); |
} |
|
std::string Disassembler::decodeSub(Word w) { |
return decodeSimpleInstruction("sub",w); |
} |
|
std::string Disassembler::decodeSw(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(dst.value()!=0) return decodeWord(w); |
if(rd1.type()!=Operand::Register) return decodeWord(w); |
|
return "sw "+rd1.str()+", "+rd2.str(); |
} |
|
std::string Disassembler::decodeXor(Word w) { |
std::ostringstream oss; |
|
auto dst=decodeDstOperand(w); |
auto rd1=decodeRd1Operand(w); |
auto rd2=decodeRd2Operand(w); |
|
if(rd2.type()==Operand::Direct&&rd2.value()==-1) |
oss<<"not "<<dst.str()<<", "<<rd1.str(); |
else |
oss<<"xor "<<dst.str()<<", "<<rd1.str()<<", "<<rd2.str(); |
|
return oss.str(); |
} |
|
std::string Disassembler::decodeWord(Word w) { |
return ".word 0x"+hex(w); |
} |
/lxp32/tags/1.0/LICENSE.md
0,0 → 1,7
Copyright (c) 2016 by Alex I. Kuznetsov |
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
/lxp32/tags/1.0/rtl/lxp32_ram256x32.vhd
0,0 → 1,66
--------------------------------------------------------------------- |
-- Generic dual-port memory |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Portable description of a dual-port memory block with one write |
-- port. Major FPGA synthesis tools can infer on-chip block RAM |
-- from this description. Can be replaced with a library component |
-- wrapper if needed. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_ram256x32 is |
port( |
wclk_i: in std_logic; |
we_i: in std_logic; |
waddr_i: in std_logic_vector(7 downto 0); |
wdata_i: in std_logic_vector(31 downto 0); |
|
rclk_i: in std_logic; |
re_i: in std_logic; |
raddr_i: in std_logic_vector(7 downto 0); |
rdata_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_ram256x32 is |
|
type ram_type is array(255 downto 0) of std_logic_vector(31 downto 0); |
signal ram: ram_type:=(others=>(others=>'0')); -- zero-initialize for SRAM-based FPGAs |
|
attribute syn_ramstyle: string; |
attribute syn_ramstyle of ram: signal is "block_ram,no_rw_check"; |
attribute ram_style: string; -- for Xilinx |
attribute ram_style of ram: signal is "block"; |
|
begin |
|
-- Write port |
|
process (wclk_i) is |
begin |
if rising_edge(wclk_i) then |
if we_i='1' then |
ram(to_integer(unsigned(waddr_i)))<=wdata_i; |
end if; |
end if; |
end process; |
|
-- Read port |
|
process (rclk_i) is |
begin |
if rising_edge(rclk_i) then |
if re_i='1' then |
rdata_o<=ram(to_integer(to_01(unsigned(raddr_i)))); |
end if; |
end if; |
end process; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_compl.vhd
0,0 → 1,50
--------------------------------------------------------------------- |
-- Complementor |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Computes a 2's complement of its input. Used as an auxiliary |
-- unit in the divider. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_compl is |
port( |
clk_i: in std_logic; |
compl_i: in std_logic; |
d_i: in std_logic_vector(31 downto 0); |
d_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_compl is |
|
signal d_prepared: unsigned(d_i'range); |
signal sum_low: unsigned(16 downto 0); |
signal d_high: unsigned(15 downto 0); |
signal sum_high: unsigned(15 downto 0); |
|
begin |
|
d_prepared_gen: for i in d_prepared'range generate |
d_prepared(i)<=d_i(i) xor compl_i; |
end generate; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
sum_low<=("0"&d_prepared(15 downto 0))+(to_unsigned(0,16)&compl_i); |
d_high<=d_prepared(31 downto 16); |
end if; |
end process; |
|
sum_high<=d_high+(to_unsigned(0,15)&sum_low(sum_low'high)); |
|
d_o<=std_logic_vector(sum_high&sum_low(15 downto 0)); |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_scratchpad.vhd
0,0 → 1,93
--------------------------------------------------------------------- |
-- Scratchpad |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- LXP32 register file implemented as a RAM block. Since we need |
-- to read two registers simultaneously, the memory is duplicated. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity lxp32_scratchpad is |
port( |
clk_i: in std_logic; |
|
raddr1_i: in std_logic_vector(7 downto 0); |
rdata1_o: out std_logic_vector(31 downto 0); |
raddr2_i: in std_logic_vector(7 downto 0); |
rdata2_o: out std_logic_vector(31 downto 0); |
|
waddr_i: in std_logic_vector(7 downto 0); |
we_i: in std_logic; |
wdata_i: in std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_scratchpad is |
|
signal wdata_reg: std_logic_vector(wdata_i'range); |
signal ram1_rdata: std_logic_vector(31 downto 0); |
signal ram2_rdata: std_logic_vector(31 downto 0); |
|
signal ram1_collision: std_logic; |
signal ram2_collision: std_logic; |
|
begin |
|
-- RAM 1 |
|
ram_inst1: entity work.lxp32_ram256x32(rtl) |
port map( |
wclk_i=>clk_i, |
we_i=>we_i, |
waddr_i=>waddr_i, |
wdata_i=>wdata_i, |
|
rclk_i=>clk_i, |
re_i=>'1', |
raddr_i=>raddr1_i, |
rdata_o=>ram1_rdata |
); |
|
-- RAM 2 |
|
ram_inst2: entity work.lxp32_ram256x32(rtl) |
port map( |
wclk_i=>clk_i, |
we_i=>we_i, |
waddr_i=>waddr_i, |
wdata_i=>wdata_i, |
|
rclk_i=>clk_i, |
re_i=>'1', |
raddr_i=>raddr2_i, |
rdata_o=>ram2_rdata |
); |
|
-- Read/write collision detection |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
wdata_reg<=wdata_i; |
if waddr_i=raddr1_i and we_i='1' then |
ram1_collision<='1'; |
else |
ram1_collision<='0'; |
end if; |
if waddr_i=raddr2_i and we_i='1' then |
ram2_collision<='1'; |
else |
ram2_collision<='0'; |
end if; |
end if; |
end process; |
|
rdata1_o<=ram1_rdata when ram1_collision='0' else wdata_reg; |
rdata2_o<=ram2_rdata when ram2_collision='0' else wdata_reg; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_icache.vhd
0,0 → 1,273
--------------------------------------------------------------------- |
-- Instruction cache |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- A simple single-page buffer providing both caching and |
-- prefetching capabilities. Useful for high-latency memory, |
-- such as external SDRAM. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_icache is |
generic( |
BURST_SIZE: integer; |
PREFETCH_SIZE: integer |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
lli_re_i: in std_logic; |
lli_adr_i: in std_logic_vector(29 downto 0); |
lli_dat_o: out std_logic_vector(31 downto 0); |
lli_busy_o: out std_logic; |
|
wbm_cyc_o: out std_logic; |
wbm_stb_o: out std_logic; |
wbm_cti_o: out std_logic_vector(2 downto 0); |
wbm_bte_o: out std_logic_vector(1 downto 0); |
wbm_ack_i: in std_logic; |
wbm_adr_o: out std_logic_vector(29 downto 0); |
wbm_dat_i: in std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_icache is |
|
signal lli_adr_reg: std_logic_vector(lli_adr_i'range); |
signal lli_adr_mux: std_logic_vector(lli_adr_i'range); |
|
signal ram_waddr: std_logic_vector(7 downto 0); |
signal ram_raddr: std_logic_vector(7 downto 0); |
signal ram_re: std_logic; |
signal ram_we: std_logic; |
|
signal read_base: unsigned(21 downto 0); |
signal read_offset: unsigned(7 downto 0); |
|
signal init: std_logic:='0'; |
signal burst1: std_logic; |
signal terminate_burst: std_logic; |
signal near_miss: std_logic:='0'; |
signal prefetch_distance: unsigned(7 downto 0); |
signal wrap_cnt: integer range 0 to 3:=0; |
signal burst_cnt: integer range 0 to BURST_SIZE:=0; |
signal wb_stb: std_logic:='0'; |
signal wb_cti: std_logic_vector(2 downto 0); |
|
-- Note: the following five signals are zero-initialized for |
-- simulation only, to suppress warnings from numeric_std. |
-- This initialization is not required for synthesis. |
|
signal current_base: unsigned(21 downto 0):=(others=>'0'); |
signal current_offset: unsigned(7 downto 0):=(others=>'0'); |
signal prev_base: unsigned(21 downto 0):=(others=>'0'); |
signal next_base: unsigned(21 downto 0):=(others=>'0'); |
signal start_offset: unsigned(7 downto 0):=(others=>'0'); |
|
signal hitc: std_logic; |
signal hitp: std_logic; |
signal miss: std_logic:='0'; |
|
begin |
|
assert PREFETCH_SIZE>=4 |
report "PREFETCH_SIZE cannot be less than 4" |
severity failure; |
assert BURST_SIZE>=4 |
report "BURST_SIZE cannot be less than 4" |
severity failure; |
assert PREFETCH_SIZE+BURST_SIZE<=128 |
report "PREFETCH_SIZE and BURST_SIZE combined cannot be greater than 128" |
severity failure; |
|
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if miss='0' then |
lli_adr_reg<=lli_adr_i; |
end if; |
end if; |
end process; |
|
lli_adr_mux<=lli_adr_i when miss='0' else lli_adr_reg; |
|
read_base<=unsigned(lli_adr_mux(29 downto 8)); |
read_offset<=unsigned(lli_adr_mux(7 downto 0)); |
|
-- Cache RAM |
|
ram_waddr<=std_logic_vector(current_offset); |
ram_raddr<=std_logic_vector(read_offset); |
ram_we<=wb_stb and wbm_ack_i; |
ram_re<=lli_re_i or miss; |
|
ram_inst: entity work.lxp32_ram256x32(rtl) |
port map( |
wclk_i=>clk_i, |
we_i=>ram_we, |
waddr_i=>ram_waddr, |
wdata_i=>wbm_dat_i, |
|
rclk_i=>clk_i, |
re_i=>ram_re, |
raddr_i=>ram_raddr, |
rdata_o=>lli_dat_o |
); |
|
-- Determine hit/miss |
|
-- This cache uses a single ring buffer. Address in buffer corresponds |
-- to the lower 8 bits of the full address. The part of the buffer that |
-- is higher than current_offset represents a previous block ("p"), the |
-- other part represents a current block ("c"). |
|
hitc<='1' when read_base=current_base and read_offset<current_offset and |
((wrap_cnt=1 and read_offset>=start_offset) or |
wrap_cnt=2 or wrap_cnt=3) else '0'; |
|
hitp<='1' when read_base=prev_base and read_offset>current_offset and |
((wrap_cnt=2 and read_offset>=start_offset) or |
wrap_cnt=3) else '0'; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
miss<='0'; |
else |
if hitc='0' and hitp='0' and ram_re='1' then |
miss<='1'; |
else |
miss<='0'; |
end if; |
end if; |
end if; |
end process; |
|
lli_busy_o<=miss; |
|
-- Set INIT flag when the first lli_re_i signal is detected |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
init<='0'; |
elsif lli_re_i='1' then |
init<='1'; |
end if; |
end if; |
end process; |
|
-- Fill cache |
|
prefetch_distance<=current_offset-read_offset; |
|
-- Note: "near_miss" signal prevents cache invalidation when difference |
-- between the requested address and the currently fetched address |
-- is too small (and, therefore, the requested data will be fetched soon |
-- without invalidation). |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
near_miss<='0'; |
elsif wrap_cnt>0 and read_offset-current_offset<=to_unsigned(BURST_SIZE/2,8) and |
((read_base=current_base and read_offset>=current_offset) or |
(read_base=next_base and read_offset<current_offset)) |
then |
near_miss<='1'; |
else |
near_miss<='0'; |
end if; |
end if; |
end process; |
|
terminate_burst<='1' when burst_cnt<BURST_SIZE-1 and miss='1' and |
(burst_cnt>2 or burst1='0') and near_miss='0' else '0'; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
burst_cnt<=0; |
wb_stb<='0'; |
wrap_cnt<=0; |
else |
if burst_cnt=0 and init='1' then |
if miss='1' and near_miss='0' then |
wb_stb<='1'; |
wb_cti<="010"; |
current_offset<=read_offset; |
start_offset<=read_offset; |
current_base<=read_base; |
next_base<=read_base+1; |
burst_cnt<=1; |
burst1<='1'; |
wrap_cnt<=1; |
elsif prefetch_distance<to_unsigned(PREFETCH_SIZE,8) or near_miss='1' then |
wb_stb<='1'; |
wb_cti<="010"; |
burst_cnt<=1; |
burst1<='0'; |
end if; |
else |
if wbm_ack_i='1' then |
current_offset<=current_offset+1; |
if current_offset=X"FF" then |
current_base<=next_base; |
next_base<=next_base+1; |
prev_base<=current_base; |
if wrap_cnt<3 then |
wrap_cnt<=wrap_cnt+1; |
end if; |
end if; |
if burst_cnt=BURST_SIZE-1 or terminate_burst='1' then |
burst_cnt<=BURST_SIZE; |
wb_cti<="111"; |
elsif burst_cnt<BURST_SIZE-1 then |
burst_cnt<=burst_cnt+1; |
wb_cti<="010"; |
else |
if miss='1' and near_miss='0' then |
wb_stb<='1'; |
wb_cti<="010"; |
current_offset<=read_offset; |
start_offset<=read_offset; |
current_base<=read_base; |
next_base<=read_base+1; |
burst_cnt<=1; |
burst1<='1'; |
wrap_cnt<=1; |
elsif prefetch_distance<to_unsigned(PREFETCH_SIZE,8) or near_miss='1' then |
wb_stb<='1'; |
wb_cti<="010"; |
burst_cnt<=1; |
burst1<='0'; |
else |
burst_cnt<=0; |
wb_stb<='0'; |
end if; |
end if; |
end if; |
end if; |
end if; |
end if; |
end process; |
|
wbm_cyc_o<=wb_stb; |
wbm_stb_o<=wb_stb; |
wbm_cti_o<=wb_cti; |
wbm_bte_o<="00"; |
wbm_adr_o<=std_logic_vector(current_base¤t_offset); |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_mul_opt.vhd
0,0 → 1,137
--------------------------------------------------------------------- |
-- Optimized multiplier |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- This multiplier is designed for technologies that don't provide |
-- fast 16x16 multipliers. One multiplication takes 6 cycles. |
-- |
-- The multiplication algorithm is based on carry-save accumulation |
-- of partial products. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_mul_opt is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
ce_i: in std_logic; |
op1_i: in std_logic_vector(31 downto 0); |
op2_i: in std_logic_vector(31 downto 0); |
ce_o: out std_logic; |
result_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_mul_opt is |
|
function csa_sum(a: unsigned; b: unsigned; c: unsigned; n: integer) return unsigned is |
variable r: unsigned(n-1 downto 0); |
begin |
for i in r'range loop |
r(i):=a(i) xor b(i) xor c(i); |
end loop; |
return r; |
end function; |
|
function csa_carry(a: unsigned; b: unsigned; c: unsigned; n: integer) return unsigned is |
variable r: unsigned(n-1 downto 0); |
begin |
for i in r'range loop |
r(i):=(a(i) and b(i)) or (a(i) and c(i)) or (b(i) and c(i)); |
end loop; |
return r&"0"; |
end function; |
|
signal reg1: unsigned(op1_i'range); |
signal reg2: unsigned(op2_i'range); |
|
type pp_type is array (7 downto 0) of unsigned(31 downto 0); |
signal pp: pp_type; |
|
type pp_sum_type is array (7 downto 0) of unsigned(31 downto 0); |
signal pp_sum: pp_sum_type; |
|
type pp_carry_type is array (7 downto 0) of unsigned(32 downto 0); |
signal pp_carry: pp_carry_type; |
|
signal acc_sum: unsigned(31 downto 0); |
signal acc_carry: unsigned(31 downto 0); |
|
signal cnt: integer range 0 to 4:=0; |
|
signal ceo: std_logic:='0'; |
|
begin |
|
-- Calculate 8 partial products in parallel |
|
pp_gen: for i in pp'range generate |
pp(i)<=shift_left(reg1,i) when reg2(i)='1' else (others=>'0'); |
end generate; |
|
-- Add partial products to the accumulator using carry-save adder tree |
|
pp_sum(0)<=csa_sum(pp(0),pp(1),pp(2),32); |
pp_carry(0)<=csa_carry(pp(0),pp(1),pp(2),32); |
|
pp_sum(1)<=csa_sum(pp(3),pp(4),pp(5),32); |
pp_carry(1)<=csa_carry(pp(3),pp(4),pp(5),32); |
|
pp_sum(2)<=csa_sum(pp(6),pp(7),acc_sum,32); |
pp_carry(2)<=csa_carry(pp(6),pp(7),acc_sum,32); |
|
pp_sum(3)<=csa_sum(pp_sum(0),pp_carry(0),pp_sum(1),32); |
pp_carry(3)<=csa_carry(pp_sum(0),pp_carry(0),pp_sum(1),32); |
|
pp_sum(4)<=csa_sum(pp_carry(1),pp_sum(2),pp_carry(2),32); |
pp_carry(4)<=csa_carry(pp_carry(1),pp_sum(2),pp_carry(2),32); |
|
pp_sum(5)<=csa_sum(pp_sum(3),pp_carry(3),pp_sum(4),32); |
pp_carry(5)<=csa_carry(pp_sum(3),pp_carry(3),pp_sum(4),32); |
|
pp_sum(6)<=csa_sum(pp_sum(5),pp_carry(5),pp_carry(4),32); |
pp_carry(6)<=csa_carry(pp_sum(5),pp_carry(5),pp_carry(4),32); |
|
pp_sum(7)<=csa_sum(pp_sum(6),pp_carry(6),acc_carry,32); |
pp_carry(7)<=csa_carry(pp_sum(6),pp_carry(6),acc_carry,32); |
|
-- Multiplier state machine |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
ceo<='0'; |
cnt<=0; |
else |
ceo<='0'; |
if ce_i='1' then |
cnt<=4; |
reg1<=unsigned(op1_i); |
reg2<=unsigned(op2_i); |
acc_sum<=(others=>'0'); |
acc_carry<=(others=>'0'); |
elsif cnt>0 then |
acc_sum<=pp_sum(7); |
acc_carry<=pp_carry(7)(acc_carry'range); |
reg1<=reg1(reg1'high-8 downto 0)&X"00"; |
reg2<=X"00"®2(reg2'high downto 8); |
cnt<=cnt-1; |
if cnt=1 then |
ceo<='1'; |
end if; |
end if; |
end if; |
end if; |
end process; |
|
result_o<=std_logic_vector(acc_sum+acc_carry); |
ce_o<=ceo; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_alu.vhd
0,0 → 1,270
--------------------------------------------------------------------- |
-- Arithmetic logic unit |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Performs arithmetic and logic operations. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_alu is |
generic( |
DIVIDER_EN: boolean; |
MUL_ARCH: string |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
valid_i: in std_logic; |
|
cmd_signed_i: in std_logic; |
cmd_addsub_i: in std_logic; |
cmd_mul_i: in std_logic; |
cmd_div_i: in std_logic; |
cmd_div_mod_i: in std_logic; |
cmd_cmp_i: in std_logic; |
cmd_negate_op2_i: in std_logic; |
cmd_and_i: in std_logic; |
cmd_or_i: in std_logic; |
cmd_xor_i: in std_logic; |
cmd_shift_i: in std_logic; |
cmd_shift_right_i: in std_logic; |
|
op1_i: in std_logic_vector(31 downto 0); |
op2_i: in std_logic_vector(31 downto 0); |
|
result_o: out std_logic_vector(31 downto 0); |
|
cmp_eq_o: out std_logic; |
cmp_ug_o: out std_logic; |
cmp_sg_o: out std_logic; |
|
we_o: out std_logic; |
busy_o: out std_logic |
); |
end entity; |
|
architecture rtl of lxp32_alu is |
|
signal addend1: unsigned(31 downto 0); |
signal addend2: unsigned(31 downto 0); |
signal adder_result: unsigned(32 downto 0); |
signal adder_we: std_logic; |
|
signal cmp_eq: std_logic; |
signal cmp_carry: std_logic; |
signal cmp_s1: std_logic; |
signal cmp_s2: std_logic; |
|
signal and_result: std_logic_vector(31 downto 0); |
signal and_we: std_logic; |
signal or_result: std_logic_vector(31 downto 0); |
signal or_we: std_logic; |
signal xor_result: std_logic_vector(31 downto 0); |
signal xor_we: std_logic; |
|
signal mul_result: std_logic_vector(31 downto 0); |
signal mul_ce: std_logic; |
signal mul_we: std_logic; |
|
signal div_quotient: std_logic_vector(31 downto 0); |
signal div_remainder: std_logic_vector(31 downto 0); |
signal div_ce: std_logic; |
signal div_we: std_logic; |
signal div_select_remainder: std_logic; |
|
signal shift_result: std_logic_vector(31 downto 0); |
signal shift_ce: std_logic; |
signal shift_we: std_logic; |
|
signal result_mux: std_logic_vector(31 downto 0); |
signal result_we: std_logic; |
|
signal busy: std_logic:='0'; |
|
begin |
|
assert MUL_ARCH="dsp" or MUL_ARCH="seq" or MUL_ARCH="opt" |
report "Invalid MUL_ARCH generic value: dsp, opt or seq expected" |
severity failure; |
|
-- Add/subtract |
|
addend1<=unsigned(op1_i); |
addend2<=unsigned(op2_i) when cmd_negate_op2_i='0' else not unsigned(op2_i); |
adder_result<=("0"&addend1)+("0"&addend2)+(to_unsigned(0,adder_result'length-1)&cmd_negate_op2_i); |
adder_we<=cmd_addsub_i and valid_i; |
|
-- Comparator (needs cmd_negate_op2_i to work correctly) |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if valid_i='1' and cmd_cmp_i='1' then |
if op1_i=op2_i then |
cmp_eq<='1'; |
else |
cmp_eq<='0'; |
end if; |
|
cmp_carry<=adder_result(adder_result'high); |
cmp_s1<=op1_i(op1_i'high); |
cmp_s2<=op2_i(op2_i'high); |
end if; |
end if; |
end process; |
|
cmp_eq_o<=cmp_eq; |
cmp_ug_o<=cmp_carry and not cmp_eq; |
cmp_sg_o<=((cmp_s1 and cmp_s2 and cmp_carry) or |
(not cmp_s1 and not cmp_s2 and cmp_carry) or |
(not cmp_s1 and cmp_s2)) and not cmp_eq; |
|
-- Logical functions |
|
and_result<=op1_i and op2_i; |
and_we<=cmd_and_i and valid_i; |
or_result<=op1_i or op2_i; |
or_we<=cmd_or_i and valid_i; |
xor_result<=op1_i xor op2_i; |
xor_we<=cmd_xor_i and valid_i; |
|
-- Multiplier |
|
mul_ce<=cmd_mul_i and valid_i; |
|
gen_mul_dsp: if MUL_ARCH="dsp" generate |
mul_inst: entity work.lxp32_mul_dsp(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
ce_i=>mul_ce, |
op1_i=>op1_i, |
op2_i=>op2_i, |
ce_o=>mul_we, |
result_o=>mul_result |
); |
end generate; |
|
gen_mul_opt: if MUL_ARCH="opt" generate |
mul_inst: entity work.lxp32_mul_opt(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
ce_i=>mul_ce, |
op1_i=>op1_i, |
op2_i=>op2_i, |
ce_o=>mul_we, |
result_o=>mul_result |
); |
end generate; |
|
gen_mul_seq: if MUL_ARCH="seq" generate |
mul_inst: entity work.lxp32_mul_seq(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
ce_i=>mul_ce, |
op1_i=>op1_i, |
op2_i=>op2_i, |
ce_o=>mul_we, |
result_o=>mul_result |
); |
end generate; |
|
-- Divider |
|
div_ce<=cmd_div_i and valid_i; |
|
gen_divider: if DIVIDER_EN generate |
divider_inst: entity work.lxp32_divider(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
ce_i=>div_ce, |
op1_i=>op1_i, |
op2_i=>op2_i, |
signed_i=>cmd_signed_i, |
ce_o=>div_we, |
quotient_o=>div_quotient, |
remainder_o=>div_remainder |
); |
end generate; |
|
gen_no_divider: if not DIVIDER_EN generate |
div_we<=div_ce; |
div_quotient<=(others=>'0'); |
div_remainder<=(others=>'0'); |
end generate; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if div_ce='1' then |
div_select_remainder<=cmd_div_mod_i; |
end if; |
end if; |
end process; |
|
-- Shifter |
|
shift_ce<=cmd_shift_i and valid_i; |
|
shifter_inst: entity work.lxp32_shifter(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
ce_i=>shift_ce, |
d_i=>op1_i, |
s_i=>op2_i(4 downto 0), |
right_i=>cmd_shift_right_i, |
sig_i=>cmd_signed_i, |
ce_o=>shift_we, |
d_o=>shift_result |
); |
|
-- Result multiplexer |
|
result_mux_gen: for i in result_mux'range generate |
result_mux(i)<=(adder_result(i) and adder_we) or |
(and_result(i) and and_we) or |
(or_result(i) and or_we) or |
(xor_result(i) and xor_we) or |
(mul_result(i) and mul_we) or |
(div_quotient(i) and div_we and not div_select_remainder) or |
(div_remainder(i) and div_we and div_select_remainder) or |
(shift_result(i) and shift_we); |
end generate; |
|
result_o<=result_mux; |
|
result_we<=adder_we or and_we or or_we or xor_we or mul_we or div_we or shift_we; |
we_o<=result_we; |
|
-- Pipeline control |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
busy<='0'; |
else |
if shift_ce='1' or mul_ce='1' or div_ce='1' then |
busy<='1'; |
end if; |
if result_we='1' then |
busy<='0'; |
end if; |
end if; |
end if; |
end process; |
|
busy_o<=busy; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_decode.vhd
0,0 → 1,319
--------------------------------------------------------------------- |
-- Instruction decoder |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- The second stage of the LXP32 pipeline. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_decode is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
word_i: in std_logic_vector(31 downto 0); |
next_ip_i: in std_logic_vector(29 downto 0); |
valid_i: in std_logic; |
jump_valid_i: in std_logic; |
ready_o: out std_logic; |
|
interrupt_valid_i: in std_logic; |
interrupt_vector_i: in std_logic_vector(2 downto 0); |
interrupt_ready_o: out std_logic; |
|
sp_raddr1_o: out std_logic_vector(7 downto 0); |
sp_rdata1_i: in std_logic_vector(31 downto 0); |
sp_raddr2_o: out std_logic_vector(7 downto 0); |
sp_rdata2_i: in std_logic_vector(31 downto 0); |
|
ready_i: in std_logic; |
valid_o: out std_logic; |
|
cmd_loadop3_o: out std_logic; |
cmd_signed_o: out std_logic; |
cmd_dbus_o: out std_logic; |
cmd_dbus_store_o: out std_logic; |
cmd_dbus_byte_o: out std_logic; |
cmd_addsub_o: out std_logic; |
cmd_mul_o: out std_logic; |
cmd_div_o: out std_logic; |
cmd_div_mod_o: out std_logic; |
cmd_cmp_o: out std_logic; |
cmd_jump_o: out std_logic; |
cmd_negate_op2_o: out std_logic; |
cmd_and_o: out std_logic; |
cmd_or_o: out std_logic; |
cmd_xor_o: out std_logic; |
cmd_shift_o: out std_logic; |
cmd_shift_right_o: out std_logic; |
|
jump_type_o: out std_logic_vector(3 downto 0); |
|
op1_o: out std_logic_vector(31 downto 0); |
op2_o: out std_logic_vector(31 downto 0); |
op3_o: out std_logic_vector(31 downto 0); |
dst_o: out std_logic_vector(7 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_decode is |
|
-- Decoder FSM state |
|
type DecoderState is (Regular,ContinueLc,ContinueCjmp,ContinueInterrupt,Halt); |
signal state: DecoderState:=Regular; |
|
-- Input instruction portions |
|
signal opcode: std_logic_vector(5 downto 0); |
signal t1: std_logic; |
signal t2: std_logic; |
signal destination: std_logic_vector(7 downto 0); |
signal rd1: std_logic_vector(7 downto 0); |
signal rd2: std_logic_vector(7 downto 0); |
|
signal current_ip: unsigned(next_ip_i'range); |
|
-- Signals related to pipeline control |
|
signal downstream_busy: std_logic; |
signal self_busy: std_logic:='0'; |
signal busy: std_logic; |
signal valid_out: std_logic:='0'; |
|
signal dst_out: std_logic_vector(7 downto 0); |
|
-- Signals related to RD operand decoding |
|
signal rd1_reg: std_logic_vector(7 downto 0); |
signal rd2_reg: std_logic_vector(7 downto 0); |
|
signal rd1_select: std_logic; |
signal rd1_direct: std_logic_vector(31 downto 0); |
signal rd2_select: std_logic; |
signal rd2_direct: std_logic_vector(31 downto 0); |
|
-- Signals related to interrupt handling |
|
signal interrupt_ready: std_logic:='0'; |
|
begin |
|
-- Dissect input word |
|
opcode<=word_i(31 downto 26); |
t1<=word_i(25); |
t2<=word_i(24); |
destination<=word_i(23 downto 16); |
rd1<=word_i(15 downto 8); |
rd2<=word_i(7 downto 0); |
|
-- Pipeline control |
|
downstream_busy<=valid_out and not ready_i; |
busy<=downstream_busy or self_busy; |
|
current_ip<=unsigned(next_ip_i)-1; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
valid_out<='0'; |
self_busy<='0'; |
state<=Regular; |
interrupt_ready<='0'; |
else |
interrupt_ready<='0'; |
if jump_valid_i='1' then |
valid_out<='0'; |
self_busy<='0'; |
state<=Regular; |
elsif downstream_busy='0' then |
case state is |
when Regular => |
cmd_loadop3_o<='0'; |
cmd_signed_o<='0'; |
cmd_dbus_o<='0'; |
cmd_dbus_store_o<='0'; |
cmd_dbus_byte_o<='0'; |
cmd_addsub_o<='0'; |
cmd_negate_op2_o<='0'; |
cmd_mul_o<='0'; |
cmd_div_o<='0'; |
cmd_div_mod_o<='0'; |
cmd_cmp_o<='0'; |
cmd_jump_o<='0'; |
cmd_and_o<='0'; |
cmd_or_o<='0'; |
cmd_xor_o<='0'; |
cmd_shift_o<='0'; |
cmd_shift_right_o<='0'; |
|
op3_o<=(others=>'-'); |
|
jump_type_o<=opcode(3 downto 0); |
|
if interrupt_valid_i='1' and valid_i='1' then |
cmd_jump_o<='1'; |
cmd_loadop3_o<='1'; |
op3_o<=std_logic_vector(current_ip)&"01"; -- LSB indicates interrupt return |
dst_out<=X"FD"; -- interrupt return pointer |
rd1_select<='1'; |
rd2_select<='0'; |
valid_out<='1'; |
interrupt_ready<='1'; |
self_busy<='1'; |
state<=ContinueInterrupt; |
else |
if opcode="000001" then |
cmd_loadop3_o<='1'; |
end if; |
|
cmd_signed_o<=opcode(0); |
|
if opcode(5 downto 3)="001" then |
cmd_dbus_o<='1'; |
end if; |
|
cmd_dbus_store_o<=opcode(2); |
cmd_dbus_byte_o<=opcode(1); |
|
if opcode(5 downto 1)="01000" then |
cmd_addsub_o<='1'; |
end if; |
|
cmd_negate_op2_o<=opcode(0); |
|
if opcode="010010" then |
cmd_mul_o<='1'; |
end if; |
|
if opcode(5 downto 2)="0101" then |
cmd_div_o<='1'; |
end if; |
|
cmd_div_mod_o<=opcode(1); |
|
if opcode="100000" then |
cmd_jump_o<='1'; |
end if; |
|
if opcode="100001" then |
cmd_jump_o<='1'; |
cmd_loadop3_o<='1'; |
op3_o<=next_ip_i&"00"; |
end if; |
|
if opcode="011000" then |
cmd_and_o<='1'; |
end if; |
|
if opcode="011001" then |
cmd_or_o<='1'; |
end if; |
|
if opcode="011010" then |
cmd_xor_o<='1'; |
end if; |
|
if opcode(5 downto 2)="0111" then |
cmd_shift_o<='1'; |
end if; |
|
cmd_shift_right_o<=opcode(1); |
|
if opcode(5 downto 4)="11" then |
cmd_cmp_o<='1'; |
cmd_negate_op2_o<='1'; |
end if; |
|
rd1_select<=t1; |
rd1_direct<=std_logic_vector(resize(signed(rd1),rd1_direct'length)); |
rd2_select<=t2; |
rd2_direct<=std_logic_vector(resize(signed(rd2),rd2_direct'length)); |
|
dst_out<=destination; |
|
if valid_i='1' then |
if opcode="000001" then |
valid_out<='0'; |
self_busy<='0'; |
state<=ContinueLc; |
elsif opcode="000010" then |
valid_out<='0'; |
self_busy<='1'; |
state<=Halt; |
elsif opcode(5 downto 4)="11" then |
valid_out<='1'; |
self_busy<='1'; |
state<=ContinueCjmp; |
else |
valid_out<='1'; |
end if; |
else |
valid_out<='0'; |
end if; |
end if; |
when ContinueLc => |
if valid_i='1' then |
valid_out<='1'; |
op3_o<=word_i; |
self_busy<='0'; |
state<=Regular; |
end if; |
when ContinueCjmp => |
valid_out<='1'; |
cmd_jump_o<='1'; |
rd1_select<='1'; |
self_busy<='0'; |
state<=Regular; |
when ContinueInterrupt => |
valid_out<='0'; |
when Halt => |
if interrupt_valid_i='1' then |
self_busy<='0'; |
state<=Regular; |
end if; |
end case; |
end if; |
end if; |
end if; |
end process; |
|
valid_o<=valid_out; |
dst_o<=dst_out; |
|
ready_o<=not busy; |
|
interrupt_ready_o<=interrupt_ready; |
|
-- Decode RD (register/direct) operands |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if busy='0' then |
rd1_reg<=rd1; |
rd2_reg<=rd2; |
end if; |
end if; |
end process; |
|
sp_raddr1_o<="11110"&interrupt_vector_i when (state=Regular and interrupt_valid_i='1' and downstream_busy='0') or state=ContinueInterrupt else |
dst_out when (state=ContinueCjmp and downstream_busy='0') else |
rd1_reg when busy='1' else |
rd1; |
|
sp_raddr2_o<=rd2_reg when busy='1' else rd2; |
|
op1_o<=sp_rdata1_i when rd1_select='1' else rd1_direct; |
op2_o<=sp_rdata2_i when rd2_select='1' else rd2_direct; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_interrupt_mux.vhd
0,0 → 1,94
--------------------------------------------------------------------- |
-- Interrupt multiplexer |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Manages LXP32 interrupts. Interrupts with lower numbers have |
-- higher priority. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_interrupt_mux is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
irq_i: in std_logic_vector(7 downto 0); |
|
interrupts_enabled_i: in std_logic_vector(7 downto 0); |
interrupts_blocked_i: in std_logic_vector(7 downto 0); |
|
interrupt_valid_o: out std_logic; |
interrupt_vector_o: out std_logic_vector(2 downto 0); |
interrupt_ready_i: in std_logic; |
interrupt_return_i: in std_logic |
); |
end entity; |
|
architecture rtl of lxp32_interrupt_mux is |
|
signal irq_reg: std_logic_vector(irq_i'range):=(others=>'0'); |
|
type state_type is (Ready,Requested,WaitForExit); |
signal state: state_type:=Ready; |
|
signal pending_interrupts: std_logic_vector(irq_i'range):=(others=>'0'); |
|
signal interrupt_valid: std_logic:='0'; |
|
begin |
|
-- Note: "disabled" interrupts (i.e. for which interrupts_enabled_i(i)='0') |
-- are ignored completely, meaning that the interrupt handler won't be |
-- called even if the interrupt is enabled later. Conversely, "blocked" |
-- interrupts are registered, but their handlers are not called until they |
-- are unblocked. |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
irq_reg<=(others=>'0'); |
pending_interrupts<=(others=>'0'); |
state<=Ready; |
interrupt_valid<='0'; |
else |
irq_reg<=irq_i; |
|
pending_interrupts<=(pending_interrupts or |
(irq_i and not irq_reg)) and |
interrupts_enabled_i; |
|
case state is |
when Ready => |
for i in pending_interrupts'reverse_range loop -- lower interrupts have priority |
if pending_interrupts(i)='1' and interrupts_blocked_i(i)='0' then |
pending_interrupts(i)<='0'; |
interrupt_valid<='1'; |
interrupt_vector_o<=std_logic_vector(to_unsigned(i,3)); |
state<=Requested; |
exit; |
end if; |
end loop; |
when Requested => |
if interrupt_ready_i='1' then |
interrupt_valid<='0'; |
state<=WaitForExit; |
end if; |
when WaitForExit => |
if interrupt_return_i='1' then |
state<=Ready; |
end if; |
end case; |
end if; |
end if; |
end process; |
|
interrupt_valid_o<=interrupt_valid; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_divider.vhd
0,0 → 1,165
--------------------------------------------------------------------- |
-- Divider |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Based on the NRD (Non Restoring Division) algorithm. One division |
-- takes 37 cycles. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_divider is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
ce_i: in std_logic; |
op1_i: in std_logic_vector(31 downto 0); |
op2_i: in std_logic_vector(31 downto 0); |
signed_i: in std_logic; |
ce_o: out std_logic; |
quotient_o: out std_logic_vector(31 downto 0); |
remainder_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_divider is |
|
-- Complementor signals |
|
signal compl1_inv: std_logic; |
signal compl2_inv: std_logic; |
signal compl1_mux: std_logic_vector(31 downto 0); |
signal compl2_mux: std_logic_vector(31 downto 0); |
signal compl1_out: std_logic_vector(31 downto 0); |
signal compl2_out: std_logic_vector(31 downto 0); |
|
signal inv_q: std_logic; |
signal inv_r: std_logic; |
|
-- Divider FSM signals |
|
signal fsm_ce: std_logic:='0'; |
|
signal dividend: unsigned(31 downto 0); |
signal divisor: unsigned(32 downto 0); |
|
signal partial_remainder: unsigned(32 downto 0); |
signal addend: unsigned(32 downto 0); |
signal sum: unsigned(32 downto 0); |
signal sum_positive: std_logic; |
signal sum_subtract: std_logic; |
|
signal cnt: integer range 0 to 34:=0; |
|
signal ceo: std_logic:='0'; |
|
-- Output restoration signals |
|
signal remainder_corrector: unsigned(31 downto 0); |
signal remainder_res: unsigned(31 downto 0); |
signal quotient_res: unsigned(31 downto 0); |
|
begin |
|
compl1_inv<=op1_i(31) and signed_i when ce_i='1' else inv_q; |
compl2_inv<=op2_i(31) and signed_i when ce_i='1' else inv_r; |
|
compl1_mux<=op1_i when ce_i='1' else std_logic_vector(quotient_res); |
compl2_mux<=op2_i when ce_i='1' else std_logic_vector(remainder_res); |
|
compl_op1_inst: entity work.lxp32_compl(rtl) |
port map( |
clk_i=>clk_i, |
compl_i=>compl1_inv, |
d_i=>compl1_mux, |
d_o=>compl1_out |
); |
|
compl_op2_inst: entity work.lxp32_compl(rtl) |
port map( |
clk_i=>clk_i, |
compl_i=>compl2_inv, |
d_i=>compl2_mux, |
d_o=>compl2_out |
); |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
fsm_ce<='0'; |
else |
fsm_ce<=ce_i; |
if ce_i='1' then |
inv_q<=(op1_i(31) xor op2_i(31)) and signed_i; |
inv_r<=op1_i(31) and signed_i; |
end if; |
end if; |
end if; |
end process; |
|
-- Main adder/subtractor |
|
addend_gen: for i in addend'range generate |
addend(i)<=divisor(i) xor sum_subtract; |
end generate; |
|
sum<=partial_remainder+addend+(to_unsigned(0,32)&sum_subtract); |
sum_positive<=not sum(32); |
|
-- Divisor state machine |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
cnt<=0; |
ceo<='0'; |
else |
ceo<='0'; |
if fsm_ce='1' then |
dividend<=unsigned(compl1_out(30 downto 0)&"0"); |
divisor<=unsigned("0"&compl2_out); |
partial_remainder<=to_unsigned(0,32)&compl1_out(31); |
sum_subtract<='1'; |
cnt<=34; |
elsif cnt>0 then |
partial_remainder<=sum(31 downto 0)÷nd(31); |
sum_subtract<=sum_positive; |
dividend<=dividend(30 downto 0)&sum_positive; |
if cnt=1 then |
ceo<='1'; |
end if; |
cnt<=cnt-1; |
else |
dividend<=(others=>'-'); |
divisor<=(others=>'-'); |
partial_remainder<=(others=>'-'); |
end if; |
end if; |
end if; |
end process; |
|
-- Output restoration circuit |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
for i in remainder_corrector'range loop |
remainder_corrector(i)<=divisor(i) and not sum_positive; |
end loop; |
quotient_res<=dividend; |
remainder_res<=partial_remainder(32 downto 1)+remainder_corrector; |
end if; |
end process; |
|
quotient_o<=compl1_out; |
remainder_o<=compl2_out; |
ce_o<=ceo; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32u_top.vhd
0,0 → 1,86
--------------------------------------------------------------------- |
-- LXP32U CPU top-level module (U-series, without instruction cache) |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- This version uses a Low Latency Interface for the instruction bus |
-- (IBUS). It is designed for low-latency slaves such as on-chip |
-- RAM blocks. |
-- |
-- Parameters: |
-- DBUS_RMW: Use RMW cycle instead of SEL_O() signal |
-- for byte-granular access to data bus |
-- DIVIDER_EN: enable divider |
-- MUL_ARCH: multiplier architecture ("dsp", "opt" |
-- or "seq") |
-- START_ADDR: address in program memory where execution |
-- starts |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity lxp32u_top is |
generic( |
DBUS_RMW: boolean:=false; |
DIVIDER_EN: boolean:=true; |
MUL_ARCH: string:="dsp"; |
START_ADDR: std_logic_vector(29 downto 0):=(others=>'0') |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
lli_re_o: out std_logic; |
lli_adr_o: out std_logic_vector(29 downto 0); |
lli_dat_i: in std_logic_vector(31 downto 0); |
lli_busy_i: in std_logic; |
|
dbus_cyc_o: out std_logic; |
dbus_stb_o: out std_logic; |
dbus_we_o: out std_logic; |
dbus_sel_o: out std_logic_vector(3 downto 0); |
dbus_ack_i: in std_logic; |
dbus_adr_o: out std_logic_vector(31 downto 2); |
dbus_dat_o: out std_logic_vector(31 downto 0); |
dbus_dat_i: in std_logic_vector(31 downto 0); |
|
irq_i: in std_logic_vector(7 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32u_top is |
|
begin |
|
cpu_inst: entity work.lxp32_cpu(rtl) |
generic map( |
DBUS_RMW=>DBUS_RMW, |
DIVIDER_EN=>DIVIDER_EN, |
MUL_ARCH=>MUL_ARCH, |
START_ADDR=>START_ADDR |
) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
lli_re_o=>lli_re_o, |
lli_adr_o=>lli_adr_o, |
lli_dat_i=>lli_dat_i, |
lli_busy_i=>lli_busy_i, |
|
dbus_cyc_o=>dbus_cyc_o, |
dbus_stb_o=>dbus_stb_o, |
dbus_we_o=>dbus_we_o, |
dbus_sel_o=>dbus_sel_o, |
dbus_ack_i=>dbus_ack_i, |
dbus_adr_o=>dbus_adr_o, |
dbus_dat_o=>dbus_dat_o, |
dbus_dat_i=>dbus_dat_i, |
|
irq_i=>irq_i |
); |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_cpu.vhd
0,0 → 1,259
--------------------------------------------------------------------- |
-- LXP32 CPU Core |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity lxp32_cpu is |
generic( |
DBUS_RMW: boolean; |
DIVIDER_EN: boolean; |
MUL_ARCH: string; |
START_ADDR: std_logic_vector(29 downto 0) |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
lli_re_o: out std_logic; |
lli_adr_o: out std_logic_vector(29 downto 0); |
lli_dat_i: in std_logic_vector(31 downto 0); |
lli_busy_i: in std_logic; |
|
dbus_cyc_o: out std_logic; |
dbus_stb_o: out std_logic; |
dbus_we_o: out std_logic; |
dbus_sel_o: out std_logic_vector(3 downto 0); |
dbus_ack_i: in std_logic; |
dbus_adr_o: out std_logic_vector(31 downto 2); |
dbus_dat_o: out std_logic_vector(31 downto 0); |
dbus_dat_i: in std_logic_vector(31 downto 0); |
|
irq_i: in std_logic_vector(7 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_cpu is |
|
signal fetch_word: std_logic_vector(31 downto 0); |
signal fetch_next_ip: std_logic_vector(29 downto 0); |
signal fetch_valid: std_logic; |
signal fetch_jump_ready: std_logic; |
|
signal decode_ready: std_logic; |
signal decode_valid: std_logic; |
|
signal decode_cmd_loadop3: std_logic; |
signal decode_cmd_signed: std_logic; |
signal decode_cmd_dbus: std_logic; |
signal decode_cmd_dbus_store: std_logic; |
signal decode_cmd_dbus_byte: std_logic; |
signal decode_cmd_addsub: std_logic; |
signal decode_cmd_mul: std_logic; |
signal decode_cmd_div: std_logic; |
signal decode_cmd_div_mod: std_logic; |
signal decode_cmd_cmp: std_logic; |
signal decode_cmd_jump: std_logic; |
signal decode_cmd_negate_op2: std_logic; |
signal decode_cmd_and: std_logic; |
signal decode_cmd_or: std_logic; |
signal decode_cmd_xor: std_logic; |
signal decode_cmd_shift: std_logic; |
signal decode_cmd_shift_right: std_logic; |
|
signal decode_jump_type: std_logic_vector(3 downto 0); |
|
signal decode_op1: std_logic_vector(31 downto 0); |
signal decode_op2: std_logic_vector(31 downto 0); |
signal decode_op3: std_logic_vector(31 downto 0); |
signal decode_dst: std_logic_vector(7 downto 0); |
|
signal execute_ready: std_logic; |
signal execute_jump_valid: std_logic; |
signal execute_jump_dst: std_logic_vector(29 downto 0); |
|
signal sp_raddr1: std_logic_vector(7 downto 0); |
signal sp_rdata1: std_logic_vector(31 downto 0); |
signal sp_raddr2: std_logic_vector(7 downto 0); |
signal sp_rdata2: std_logic_vector(31 downto 0); |
signal sp_waddr: std_logic_vector(7 downto 0); |
signal sp_we: std_logic; |
signal sp_wdata: std_logic_vector(31 downto 0); |
|
signal interrupt_valid: std_logic; |
signal interrupt_vector: std_logic_vector(2 downto 0); |
signal interrupt_ready: std_logic; |
signal interrupt_return: std_logic; |
signal interrupts_enabled: std_logic_vector(7 downto 0); |
signal interrupts_blocked: std_logic_vector(7 downto 0); |
|
begin |
|
fetch_inst: entity work.lxp32_fetch(rtl) |
generic map( |
START_ADDR=>START_ADDR |
) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
lli_re_o=>lli_re_o, |
lli_adr_o=>lli_adr_o, |
lli_dat_i=>lli_dat_i, |
lli_busy_i=>lli_busy_i, |
|
word_o=>fetch_word, |
next_ip_o=>fetch_next_ip, |
valid_o=>fetch_valid, |
ready_i=>decode_ready, |
|
jump_valid_i=>execute_jump_valid, |
jump_dst_i=>execute_jump_dst, |
jump_ready_o=>fetch_jump_ready |
); |
|
decode_inst: entity work.lxp32_decode(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
word_i=>fetch_word, |
next_ip_i=>fetch_next_ip, |
valid_i=>fetch_valid, |
jump_valid_i=>execute_jump_valid, |
ready_o=>decode_ready, |
|
interrupt_valid_i=>interrupt_valid, |
interrupt_vector_i=>interrupt_vector, |
interrupt_ready_o=>interrupt_ready, |
|
sp_raddr1_o=>sp_raddr1, |
sp_rdata1_i=>sp_rdata1, |
sp_raddr2_o=>sp_raddr2, |
sp_rdata2_i=>sp_rdata2, |
|
ready_i=>execute_ready, |
valid_o=>decode_valid, |
|
cmd_loadop3_o=>decode_cmd_loadop3, |
cmd_signed_o=>decode_cmd_signed, |
cmd_dbus_o=>decode_cmd_dbus, |
cmd_dbus_store_o=>decode_cmd_dbus_store, |
cmd_dbus_byte_o=>decode_cmd_dbus_byte, |
cmd_addsub_o=>decode_cmd_addsub, |
cmd_mul_o=>decode_cmd_mul, |
cmd_div_o=>decode_cmd_div, |
cmd_div_mod_o=>decode_cmd_div_mod, |
cmd_cmp_o=>decode_cmd_cmp, |
cmd_jump_o=>decode_cmd_jump, |
cmd_negate_op2_o=>decode_cmd_negate_op2, |
cmd_and_o=>decode_cmd_and, |
cmd_or_o=>decode_cmd_or, |
cmd_xor_o=>decode_cmd_xor, |
cmd_shift_o=>decode_cmd_shift, |
cmd_shift_right_o=>decode_cmd_shift_right, |
|
jump_type_o=>decode_jump_type, |
|
op1_o=>decode_op1, |
op2_o=>decode_op2, |
op3_o=>decode_op3, |
dst_o=>decode_dst |
); |
|
execute_inst: entity work.lxp32_execute(rtl) |
generic map( |
DBUS_RMW=>DBUS_RMW, |
DIVIDER_EN=>DIVIDER_EN, |
MUL_ARCH=>MUL_ARCH |
) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
cmd_loadop3_i=>decode_cmd_loadop3, |
cmd_signed_i=>decode_cmd_signed, |
cmd_dbus_i=>decode_cmd_dbus, |
cmd_dbus_store_i=>decode_cmd_dbus_store, |
cmd_dbus_byte_i=>decode_cmd_dbus_byte, |
cmd_addsub_i=>decode_cmd_addsub, |
cmd_mul_i=>decode_cmd_mul, |
cmd_div_i=>decode_cmd_div, |
cmd_div_mod_i=>decode_cmd_div_mod, |
cmd_cmp_i=>decode_cmd_cmp, |
cmd_jump_i=>decode_cmd_jump, |
cmd_negate_op2_i=>decode_cmd_negate_op2, |
cmd_and_i=>decode_cmd_and, |
cmd_or_i=>decode_cmd_or, |
cmd_xor_i=>decode_cmd_xor, |
cmd_shift_i=>decode_cmd_shift, |
cmd_shift_right_i=>decode_cmd_shift_right, |
|
jump_type_i=>decode_jump_type, |
|
op1_i=>decode_op1, |
op2_i=>decode_op2, |
op3_i=>decode_op3, |
dst_i=>decode_dst, |
|
sp_waddr_o=>sp_waddr, |
sp_we_o=>sp_we, |
sp_wdata_o=>sp_wdata, |
|
valid_i=>decode_valid, |
ready_o=>execute_ready, |
|
dbus_cyc_o=>dbus_cyc_o, |
dbus_stb_o=>dbus_stb_o, |
dbus_we_o=>dbus_we_o, |
dbus_sel_o=>dbus_sel_o, |
dbus_ack_i=>dbus_ack_i, |
dbus_adr_o=>dbus_adr_o, |
dbus_dat_o=>dbus_dat_o, |
dbus_dat_i=>dbus_dat_i, |
|
jump_valid_o=>execute_jump_valid, |
jump_dst_o=>execute_jump_dst, |
jump_ready_i=>fetch_jump_ready, |
|
interrupt_return_o=>interrupt_return, |
interrupts_enabled_o=>interrupts_enabled, |
interrupts_blocked_o=>interrupts_blocked |
); |
|
scratchpad_inst: entity work.lxp32_scratchpad(rtl) |
port map( |
clk_i=>clk_i, |
|
raddr1_i=>sp_raddr1, |
rdata1_o=>sp_rdata1, |
raddr2_i=>sp_raddr2, |
rdata2_o=>sp_rdata2, |
|
waddr_i=>sp_waddr, |
we_i=>sp_we, |
wdata_i=>sp_wdata |
); |
|
interrupt_mux_inst: entity work.lxp32_interrupt_mux(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
irq_i=>irq_i, |
|
interrupts_enabled_i=>interrupts_enabled, |
interrupts_blocked_i=>interrupts_blocked, |
|
interrupt_valid_o=>interrupt_valid, |
interrupt_vector_o=>interrupt_vector, |
interrupt_ready_i=>interrupt_ready, |
interrupt_return_i=>interrupt_return |
); |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_fetch.vhd
0,0 → 1,162
--------------------------------------------------------------------- |
-- Instruction fetch |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- The first stage of the LXP32 pipeline. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_fetch is |
generic( |
START_ADDR: std_logic_vector(29 downto 0) |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
lli_re_o: out std_logic; |
lli_adr_o: out std_logic_vector(29 downto 0); |
lli_dat_i: in std_logic_vector(31 downto 0); |
lli_busy_i: in std_logic; |
|
word_o: out std_logic_vector(31 downto 0); |
next_ip_o: out std_logic_vector(29 downto 0); |
valid_o: out std_logic; |
ready_i: in std_logic; |
|
jump_valid_i: in std_logic; |
jump_dst_i: in std_logic_vector(29 downto 0); |
jump_ready_o: out std_logic |
); |
end entity; |
|
architecture rtl of lxp32_fetch is |
|
signal init: std_logic:='1'; |
signal init_cnt: unsigned(7 downto 0):=(others=>'0'); |
|
signal fetch_addr: std_logic_vector(29 downto 0):=START_ADDR; |
|
signal next_word: std_logic; |
signal suppress_re: std_logic:='0'; |
signal re: std_logic; |
signal requested: std_logic:='0'; |
|
signal fifo_rst: std_logic; |
signal fifo_we: std_logic; |
signal fifo_din: std_logic_vector(61 downto 0); |
signal fifo_re: std_logic; |
signal fifo_dout: std_logic_vector(61 downto 0); |
signal fifo_empty: std_logic; |
signal fifo_full: std_logic; |
|
signal jr: std_logic:='0'; |
|
begin |
|
-- INIT state machine (to initialize all registers) |
|
-- All CPU registers are expected to be zero-initialized after reset. |
-- Since these registers are implemented as a RAM block, we perform |
-- the initialization sequentially by generating "mov rN, 0" instructions |
-- for each N from 0 to 255. |
-- |
-- With SRAM-based FPGAs, flip-flops and RAM blocks have deterministic |
-- state after configuration. On these technologies the CPU can operate |
-- without reset and the initialization procedure described above is not |
-- needed. However, the initialization is still performed as usual when |
-- external reset signal is asserted. |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
init<='0'; |
init_cnt<=(others=>'0'); |
else |
if init='0' and ready_i='1' then |
init_cnt<=init_cnt+1; |
if init_cnt=X"FF" then |
init<='1'; |
end if; |
end if; |
end if; |
end if; |
end process; |
|
-- FETCH state machine |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
fetch_addr<=START_ADDR; |
requested<='0'; |
jr<='0'; |
suppress_re<='0'; |
else |
jr<='0'; |
-- Suppress LLI request if jump signal is active but will not be processed |
-- in this cycle. Helps to reduce jump latency with high-latency LLI slaves. |
-- Note: gating "re" with "jump_valid_i and not jr" asynchronously would |
-- reduce jump latency even more, but we really want to avoid too large |
-- clock-to-out on LLI outputs. |
suppress_re<=jump_valid_i and not jr and not next_word; |
if lli_busy_i='0' then |
requested<=re and not (jump_valid_i and not jr); |
end if; |
if next_word='1' then |
if jump_valid_i='1' and jr='0' then |
fetch_addr<=jump_dst_i; |
jr<='1'; |
else |
fetch_addr<=std_logic_vector(unsigned(fetch_addr)+1); |
end if; |
end if; |
end if; |
end if; |
end process; |
|
next_word<=(fifo_empty or ready_i) and not lli_busy_i and init; |
re<=(fifo_empty or ready_i) and init and not suppress_re; |
lli_re_o<=re; |
lli_adr_o<=fetch_addr; |
|
jump_ready_o<=jr; |
|
-- Small instruction buffer |
|
fifo_rst<=rst_i or (jump_valid_i and not jr); |
fifo_we<=requested and not lli_busy_i; |
fifo_din<=fetch_addr&lli_dat_i; |
fifo_re<=ready_i and not fifo_empty; |
|
ubuf_inst: entity work.lxp32_ubuf(rtl) |
generic map( |
DATA_WIDTH=>62 |
) |
port map( |
clk_i=>clk_i, |
rst_i=>fifo_rst, |
|
we_i=>fifo_we, |
d_i=>fifo_din, |
re_i=>fifo_re, |
d_o=>fifo_dout, |
|
empty_o=>fifo_empty, |
full_o=>fifo_full |
); |
|
next_ip_o<=fifo_dout(61 downto 32); |
|
word_o<=fifo_dout(31 downto 0) when init='1' else X"40"&std_logic_vector(init_cnt)&X"0000"; |
valid_o<=not fifo_empty or not init; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_dbus.vhd
0,0 → 1,163
--------------------------------------------------------------------- |
-- DBUS master |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Manages data bus (DBUS) access. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_dbus is |
generic( |
RMW: boolean |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
valid_i: in std_logic; |
|
cmd_dbus_i: in std_logic; |
cmd_dbus_store_i: in std_logic; |
cmd_dbus_byte_i: in std_logic; |
cmd_signed_i: in std_logic; |
addr_i: in std_logic_vector(31 downto 0); |
wdata_i: in std_logic_vector(31 downto 0); |
|
rdata_o: out std_logic_vector(31 downto 0); |
we_o: out std_logic; |
busy_o: out std_logic; |
|
dbus_cyc_o: out std_logic; |
dbus_stb_o: out std_logic; |
dbus_we_o: out std_logic; |
dbus_sel_o: out std_logic_vector(3 downto 0); |
dbus_ack_i: in std_logic; |
dbus_adr_o: out std_logic_vector(31 downto 2); |
dbus_dat_o: out std_logic_vector(31 downto 0); |
dbus_dat_i: in std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_dbus is |
|
signal strobe: std_logic:='0'; |
signal we_out: std_logic:='0'; |
signal we: std_logic; |
signal byte_mode: std_logic; |
signal sel: std_logic_vector(3 downto 0); |
signal sig: std_logic; |
signal rmw_mode: std_logic; |
|
signal selected_byte: std_logic_vector(7 downto 0); |
|
begin |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
we_out<='0'; |
strobe<='0'; |
else |
we_out<='0'; |
if strobe='0' then |
if valid_i='1' and cmd_dbus_i='1' then |
strobe<='1'; |
sig<=cmd_signed_i; |
|
dbus_adr_o<=addr_i(31 downto 2); |
dbus_dat_o<=wdata_i; |
|
if cmd_dbus_byte_i='0' then |
byte_mode<='0'; |
sel<="1111"; |
|
-- synthesis translate_off |
assert addr_i(1 downto 0)="00" |
report "Misaligned word-granular access on data bus" |
severity warning; |
-- synthesis translate_on |
else |
byte_mode<='1'; |
case addr_i(1 downto 0) is |
when "00" => sel<="0001"; dbus_dat_o(7 downto 0)<=wdata_i(7 downto 0); |
when "01" => sel<="0010"; dbus_dat_o(15 downto 8)<=wdata_i(7 downto 0); |
when "10" => sel<="0100"; dbus_dat_o(23 downto 16)<=wdata_i(7 downto 0); |
when "11" => sel<="1000"; dbus_dat_o(31 downto 24)<=wdata_i(7 downto 0); |
when others => |
end case; |
end if; |
|
if not RMW then |
we<=cmd_dbus_store_i; |
rmw_mode<='0'; |
else |
we<=cmd_dbus_store_i and not cmd_dbus_byte_i; |
rmw_mode<=cmd_dbus_store_i and cmd_dbus_byte_i; |
end if; |
end if; |
else |
if dbus_ack_i='1' then |
if rmw_mode='1' and we='0' and RMW then |
we<='1'; |
for i in sel'range loop |
if sel(i)='0' then |
dbus_dat_o(i*8+7 downto i*8)<= |
dbus_dat_i(i*8+7 downto i*8); |
end if; |
end loop; |
else |
strobe<='0'; |
if we='0' then |
we_out<='1'; |
end if; |
end if; |
end if; |
end if; |
end if; |
end if; |
end process; |
|
dbus_cyc_o<=strobe; |
dbus_stb_o<=strobe; |
dbus_we_o<=we; |
|
sel_no_rmw_gen: if not RMW generate |
dbus_sel_o<=sel; |
end generate; |
|
sel_rmw_gen: if RMW generate |
dbus_sel_o<=(others=>'1'); |
end generate; |
|
selected_byte_gen: for i in selected_byte'range generate |
selected_byte(i)<=(dbus_dat_i(i) and sel(0)) or |
(dbus_dat_i(i+8) and sel(1)) or |
(dbus_dat_i(i+16) and sel(2)) or |
(dbus_dat_i(i+24) and sel(3)); |
end generate; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if byte_mode='0' then |
rdata_o<=dbus_dat_i; |
else |
rdata_o(7 downto 0)<=selected_byte; |
for i in rdata_o'high downto 8 loop |
rdata_o(i)<=selected_byte(selected_byte'high) and sig; |
end loop; |
end if; |
end if; |
end process; |
|
we_o<=we_out; |
busy_o<=strobe or we_out; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_ubuf.vhd
0,0 → 1,87
--------------------------------------------------------------------- |
-- Microbuffer |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- A small buffer with a FIFO-like interface, implemented |
-- using registers. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity lxp32_ubuf is |
generic( |
DATA_WIDTH: integer |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
we_i: in std_logic; |
d_i: in std_logic_vector(DATA_WIDTH-1 downto 0); |
re_i: in std_logic; |
d_o: out std_logic_vector(DATA_WIDTH-1 downto 0); |
|
empty_o: out std_logic; |
full_o: out std_logic |
); |
end entity; |
|
architecture rtl of lxp32_ubuf is |
|
signal we: std_logic; |
signal re: std_logic; |
|
signal empty: std_logic:='1'; |
signal full: std_logic:='0'; |
|
type regs_type is array (1 downto 0) of std_logic_vector(DATA_WIDTH-1 downto 0); |
signal regs: regs_type; |
signal regs_mux: regs_type; |
|
signal wpointer: std_logic_vector(2 downto 0):="001"; |
|
begin |
|
we<=we_i and not full; |
re<=re_i and not empty; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
wpointer<="001"; |
empty<='1'; |
full<='0'; |
else |
if re='0' then |
regs<=regs_mux; |
else |
regs(0)<=regs_mux(1); |
end if; |
|
if we='1' and re='0' then |
wpointer<=wpointer(1 downto 0)&"0"; |
empty<='0'; |
full<=wpointer(1); |
elsif we='0' and re='1' then |
wpointer<="0"&wpointer(2 downto 1); |
empty<=wpointer(1); |
full<='0'; |
end if; |
end if; |
end if; |
end process; |
|
mux: for i in regs_mux'range generate |
regs_mux(i)<=regs(i) when we='0' or wpointer(i)='0' else d_i; |
end generate; |
|
d_o<=regs(0); |
empty_o<=empty; |
full_o<=full; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_execute.vhd
0,0 → 1,283
--------------------------------------------------------------------- |
-- Execution unit |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- The third stage of the LXP32 pipeline. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity lxp32_execute is |
generic( |
DBUS_RMW: boolean; |
DIVIDER_EN: boolean; |
MUL_ARCH: string |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
cmd_loadop3_i: in std_logic; |
cmd_signed_i: in std_logic; |
cmd_dbus_i: in std_logic; |
cmd_dbus_store_i: in std_logic; |
cmd_dbus_byte_i: in std_logic; |
cmd_addsub_i: in std_logic; |
cmd_mul_i: in std_logic; |
cmd_div_i: in std_logic; |
cmd_div_mod_i: in std_logic; |
cmd_cmp_i: in std_logic; |
cmd_jump_i: in std_logic; |
cmd_negate_op2_i: in std_logic; |
cmd_and_i: in std_logic; |
cmd_or_i: in std_logic; |
cmd_xor_i: in std_logic; |
cmd_shift_i: in std_logic; |
cmd_shift_right_i: in std_logic; |
|
jump_type_i: in std_logic_vector(3 downto 0); |
|
op1_i: in std_logic_vector(31 downto 0); |
op2_i: in std_logic_vector(31 downto 0); |
op3_i: in std_logic_vector(31 downto 0); |
dst_i: in std_logic_vector(7 downto 0); |
|
sp_waddr_o: out std_logic_vector(7 downto 0); |
sp_we_o: out std_logic; |
sp_wdata_o: out std_logic_vector(31 downto 0); |
|
valid_i: in std_logic; |
ready_o: out std_logic; |
|
dbus_cyc_o: out std_logic; |
dbus_stb_o: out std_logic; |
dbus_we_o: out std_logic; |
dbus_sel_o: out std_logic_vector(3 downto 0); |
dbus_ack_i: in std_logic; |
dbus_adr_o: out std_logic_vector(31 downto 2); |
dbus_dat_o: out std_logic_vector(31 downto 0); |
dbus_dat_i: in std_logic_vector(31 downto 0); |
|
jump_valid_o: out std_logic; |
jump_dst_o: out std_logic_vector(29 downto 0); |
jump_ready_i: in std_logic; |
|
interrupt_return_o: out std_logic; |
interrupts_enabled_o: out std_logic_vector(7 downto 0); |
interrupts_blocked_o: out std_logic_vector(7 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_execute is |
|
-- Pipeline control signals |
|
signal busy: std_logic; |
signal can_execute: std_logic; |
|
-- ALU signals |
|
signal alu_result: std_logic_vector(31 downto 0); |
signal alu_we: std_logic; |
signal alu_busy: std_logic; |
|
signal alu_cmp_eq: std_logic; |
signal alu_cmp_ug: std_logic; |
signal alu_cmp_sg: std_logic; |
|
-- OP3 loader signals |
|
signal loadop3_we: std_logic; |
|
-- Jump machine signals |
|
signal jump_condition: std_logic; |
signal jump_valid: std_logic:='0'; |
signal jump_dst: std_logic_vector(jump_dst_o'range); |
|
-- DBUS signals |
|
signal dbus_result: std_logic_vector(31 downto 0); |
signal dbus_busy: std_logic; |
signal dbus_we: std_logic; |
|
-- Result mux signals |
|
signal result_mux: std_logic_vector(31 downto 0); |
signal result_valid: std_logic; |
signal result_regaddr: std_logic_vector(7 downto 0); |
|
signal dst_reg: std_logic_vector(7 downto 0); |
|
-- Signals related to interrupt handling |
|
signal interrupt_return: std_logic:='0'; |
signal interrupts_enabled: std_logic_vector(7 downto 0):=(others=>'0'); |
signal interrupts_blocked: std_logic_vector(7 downto 0):=(others=>'0'); |
|
begin |
|
-- Pipeline control |
|
busy<=alu_busy or dbus_busy; |
ready_o<=not busy; |
can_execute<=valid_i and not busy; |
|
-- ALU |
|
alu_inst: entity work.lxp32_alu(rtl) |
generic map( |
DIVIDER_EN=>DIVIDER_EN, |
MUL_ARCH=>MUL_ARCH |
) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
valid_i=>can_execute, |
|
cmd_signed_i=>cmd_signed_i, |
cmd_addsub_i=>cmd_addsub_i, |
cmd_mul_i=>cmd_mul_i, |
cmd_div_i=>cmd_div_i, |
cmd_div_mod_i=>cmd_div_mod_i, |
cmd_cmp_i=>cmd_cmp_i, |
cmd_negate_op2_i=>cmd_negate_op2_i, |
cmd_and_i=>cmd_and_i, |
cmd_or_i=>cmd_or_i, |
cmd_xor_i=>cmd_xor_i, |
cmd_shift_i=>cmd_shift_i, |
cmd_shift_right_i=>cmd_shift_right_i, |
|
op1_i=>op1_i, |
op2_i=>op2_i, |
|
result_o=>alu_result, |
|
cmp_eq_o=>alu_cmp_eq, |
cmp_ug_o=>alu_cmp_ug, |
cmp_sg_o=>alu_cmp_sg, |
|
we_o=>alu_we, |
busy_o=>alu_busy |
); |
|
-- OP3 loader |
|
loadop3_we<=can_execute and cmd_loadop3_i; |
|
-- Jump logic |
|
jump_condition<=(not cmd_cmp_i) or (jump_type_i(3) and alu_cmp_eq) or |
(jump_type_i(2) and not alu_cmp_eq) or (jump_type_i(1) and alu_cmp_ug) or |
(jump_type_i(0) and alu_cmp_sg); |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
jump_valid<='0'; |
interrupt_return<='0'; |
else |
if jump_valid='0' then |
if can_execute='1' and cmd_jump_i='1' and jump_condition='1' then |
jump_valid<='1'; |
jump_dst<=op1_i(31 downto 2); |
interrupt_return<=op1_i(0); |
end if; |
elsif jump_ready_i='1' then |
jump_valid<='0'; |
interrupt_return<='0'; |
end if; |
end if; |
end if; |
end process; |
|
jump_valid_o<=jump_valid or (can_execute and cmd_jump_i and jump_condition); |
jump_dst_o<=jump_dst when jump_valid='1' else op1_i(31 downto 2); |
|
interrupt_return_o<=interrupt_return; |
|
-- DBUS access |
|
dbus_inst: entity work.lxp32_dbus(rtl) |
generic map( |
RMW=>DBUS_RMW |
) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
valid_i=>can_execute, |
|
cmd_dbus_i=>cmd_dbus_i, |
cmd_dbus_store_i=>cmd_dbus_store_i, |
cmd_dbus_byte_i=>cmd_dbus_byte_i, |
cmd_signed_i=>cmd_signed_i, |
addr_i=>op1_i, |
wdata_i=>op2_i, |
|
rdata_o=>dbus_result, |
busy_o=>dbus_busy, |
we_o=>dbus_we, |
|
dbus_cyc_o=>dbus_cyc_o, |
dbus_stb_o=>dbus_stb_o, |
dbus_we_o=>dbus_we_o, |
dbus_sel_o=>dbus_sel_o, |
dbus_ack_i=>dbus_ack_i, |
dbus_adr_o=>dbus_adr_o, |
dbus_dat_o=>dbus_dat_o, |
dbus_dat_i=>dbus_dat_i |
); |
|
-- Result multiplexer |
|
result_mux_gen: for i in result_mux'range generate |
result_mux(i)<=(alu_result(i) and alu_we) or |
(op3_i(i) and loadop3_we) or |
(dbus_result(i) and dbus_we); |
end generate; |
|
result_valid<=alu_we or loadop3_we or dbus_we; |
|
-- Write destination register |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if can_execute='1' then |
dst_reg<=dst_i; |
end if; |
end if; |
end process; |
|
result_regaddr<=dst_i when can_execute='1' else dst_reg; |
|
sp_we_o<=result_valid; |
sp_waddr_o<=result_regaddr; |
sp_wdata_o<=result_mux; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
interrupts_enabled<=(others=>'0'); |
interrupts_blocked<=(others=>'0'); |
else |
if result_valid='1' and result_regaddr=X"FC" then |
interrupts_enabled<=result_mux(7 downto 0); |
interrupts_blocked<=result_mux(15 downto 8); |
end if; |
end if; |
end if; |
end process; |
|
interrupts_enabled_o<=interrupts_enabled; |
interrupts_blocked_o<=interrupts_blocked; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_mul_dsp.vhd
0,0 → 1,82
--------------------------------------------------------------------- |
-- DSP multiplier |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- This multiplier is designed for technologies that provide fast |
-- 16x16 multipliers, including most modern FPGA families. One |
-- multiplication takes 2 cycles. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_mul_dsp is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
ce_i: in std_logic; |
op1_i: in std_logic_vector(31 downto 0); |
op2_i: in std_logic_vector(31 downto 0); |
ce_o: out std_logic; |
result_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_mul_dsp is |
|
signal pp00: unsigned(31 downto 0); |
signal pp01: unsigned(31 downto 0); |
signal pp10: unsigned(31 downto 0); |
|
signal product: unsigned(31 downto 0); |
|
signal ceo: std_logic:='0'; |
|
begin |
|
mul00_inst: entity work.lxp32_mul16x16 |
port map( |
clk_i=>clk_i, |
a_i=>op1_i(15 downto 0), |
b_i=>op2_i(15 downto 0), |
unsigned(p_o)=>pp00 |
); |
|
mul01_inst: entity work.lxp32_mul16x16 |
port map( |
clk_i=>clk_i, |
a_i=>op1_i(15 downto 0), |
b_i=>op2_i(31 downto 16), |
unsigned(p_o)=>pp01 |
); |
|
mul10_inst: entity work.lxp32_mul16x16 |
port map( |
clk_i=>clk_i, |
a_i=>op1_i(31 downto 16), |
b_i=>op2_i(15 downto 0), |
unsigned(p_o)=>pp10 |
); |
|
product(31 downto 16)<=pp00(31 downto 16)+pp01(15 downto 0)+pp10(15 downto 0); |
product(15 downto 0)<=pp00(15 downto 0); |
result_o<=std_logic_vector(product); |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
ceo<='0'; |
else |
ceo<=ce_i; |
end if; |
end if; |
end process; |
|
ce_o<=ceo; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_mul16x16.vhd
0,0 → 1,36
--------------------------------------------------------------------- |
-- A basic parallel 16x16 multiplier with an output register |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- A straightforward behavioral description. Can be replaced |
-- with a library component wrapper if needed. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_mul16x16 is |
port( |
clk_i: in std_logic; |
a_i: in std_logic_vector(15 downto 0); |
b_i: in std_logic_vector(15 downto 0); |
p_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_mul16x16 is |
|
begin |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
p_o<=std_logic_vector(unsigned(a_i)*unsigned(b_i)); |
end if; |
end process; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_shifter.vhd
0,0 → 1,95
--------------------------------------------------------------------- |
-- Barrel shifter |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Performs logical (unsigned) and arithmetic (signed) shifts |
-- in both directions. Pipeline latency: 1 cycle. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity lxp32_shifter is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
ce_i: in std_logic; |
d_i: in std_logic_vector(31 downto 0); |
s_i: in std_logic_vector(4 downto 0); |
right_i: in std_logic; |
sig_i: in std_logic; |
ce_o: out std_logic; |
d_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_shifter is |
|
signal data: std_logic_vector(d_i'range); |
signal data_shifted: std_logic_vector(d_i'range); |
|
signal fill: std_logic; -- 0 for unsigned shifts, sign bit for signed ones |
signal fill_v: std_logic_vector(3 downto 0); |
|
type cascades_type is array (4 downto 0) of std_logic_vector(d_i'range); |
signal cascades: cascades_type; |
|
signal stage2_data: std_logic_vector(d_i'range); |
signal stage2_s: std_logic_vector(s_i'range); |
signal stage2_fill: std_logic; |
signal stage2_fill_v: std_logic_vector(15 downto 0); |
signal stage2_right: std_logic; |
|
signal ceo: std_logic:='0'; |
|
begin |
|
-- Internally, data are shifted in left direction. For right shifts |
-- we reverse the argument's bit order |
|
data_gen: for i in data'range generate |
data(i)<=d_i(i) when right_i='0' else d_i(d_i'high-i); |
end generate; |
|
-- A set of cascaded shifters shifting by powers of two |
|
fill<=sig_i and data(0); |
fill_v<=(others=>fill); |
|
cascades(0)<=data(30 downto 0)&fill_v(0) when s_i(0)='1' else data; |
cascades(1)<=cascades(0)(29 downto 0)&fill_v(1 downto 0) when s_i(1)='1' else cascades(0); |
cascades(2)<=cascades(1)(27 downto 0)&fill_v(3 downto 0) when s_i(2)='1' else cascades(1); |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
ceo<='0'; |
else |
ceo<=ce_i; |
stage2_data<=cascades(2); |
stage2_s<=s_i; |
stage2_fill<=fill; |
stage2_right<=right_i; |
end if; |
end if; |
end process; |
|
stage2_fill_v<=(others=>stage2_fill); |
|
cascades(3)<=stage2_data(23 downto 0)&stage2_fill_v(7 downto 0) when stage2_s(3)='1' else stage2_data; |
cascades(4)<=cascades(3)(15 downto 0)&stage2_fill_v(15 downto 0) when stage2_s(4)='1' else cascades(3); |
|
-- Reverse bit order back, if needed |
|
data_shifted_gen: for i in data_shifted'range generate |
data_shifted(i)<=cascades(4)(i) when stage2_right='0' else cascades(4)(cascades(4)'high-i); |
end generate; |
|
d_o<=data_shifted; |
ce_o<=ceo; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32_mul_seq.vhd
0,0 → 1,70
--------------------------------------------------------------------- |
-- Sequential multiplier |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- The smallest possible multiplier. Implemented using |
-- an accumulator. One multiplication takes 34 cycles. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity lxp32_mul_seq is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
ce_i: in std_logic; |
op1_i: in std_logic_vector(31 downto 0); |
op2_i: in std_logic_vector(31 downto 0); |
ce_o: out std_logic; |
result_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32_mul_seq is |
|
signal reg1: unsigned(op1_i'range); |
signal reg2: unsigned(op2_i'range); |
signal pp: unsigned(31 downto 0); |
signal acc_sum: unsigned(31 downto 0); |
signal cnt: integer range 0 to 32:=0; |
signal ceo: std_logic:='0'; |
|
begin |
|
pp<=reg1 when reg2(0)='1' else (others=>'0'); |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
ceo<='0'; |
cnt<=0; |
else |
ceo<='0'; |
if ce_i='1' then |
cnt<=32; |
reg1<=unsigned(op1_i); |
reg2<=unsigned(op2_i); |
acc_sum<=(others=>'0'); |
elsif cnt>0 then |
acc_sum<=acc_sum+pp; |
reg1<=reg1(reg1'high-1 downto 0)&"0"; |
reg2<="0"®2(reg2'high downto 1); |
cnt<=cnt-1; |
if cnt=1 then |
ceo<='1'; |
end if; |
end if; |
end if; |
end if; |
end process; |
|
result_o<=std_logic_vector(acc_sum); |
ce_o<=ceo; |
|
end architecture; |
/lxp32/tags/1.0/rtl/lxp32c_top.vhd
0,0 → 1,122
--------------------------------------------------------------------- |
-- LXP32C CPU top-level module (C-series, with instruction cache) |
-- |
-- Part of the LXP32 CPU |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- This version uses Wishbone B3 interface for the instruction bus |
-- (IBUS). It is designed for high-latency program memory, such as |
-- external SDRAM chips. |
-- |
-- Parameters: |
-- DBUS_RMW: Use RMW cycle instead of SEL_O() signal |
-- for byte-granular access to data bus |
-- DIVIDER_EN: enable divider |
-- IBUS_BURST_SIZE: size of the burst |
-- IBUS_PREFETCH_SIZE: initiate read burst if number of words |
-- left in the buffer is less than specified |
-- MUL_ARCH: multiplier architecture ("dsp", "opt" |
-- or "seq") |
-- START_ADDR: address in program memory where execution |
-- starts |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity lxp32c_top is |
generic( |
DBUS_RMW: boolean:=false; |
DIVIDER_EN: boolean:=true; |
IBUS_BURST_SIZE: integer:=16; |
IBUS_PREFETCH_SIZE: integer:=32; |
MUL_ARCH: string:="dsp"; |
START_ADDR: std_logic_vector(29 downto 0):=(others=>'0') |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
ibus_cyc_o: out std_logic; |
ibus_stb_o: out std_logic; |
ibus_cti_o: out std_logic_vector(2 downto 0); |
ibus_bte_o: out std_logic_vector(1 downto 0); |
ibus_ack_i: in std_logic; |
ibus_adr_o: out std_logic_vector(29 downto 0); |
ibus_dat_i: in std_logic_vector(31 downto 0); |
|
dbus_cyc_o: out std_logic; |
dbus_stb_o: out std_logic; |
dbus_we_o: out std_logic; |
dbus_sel_o: out std_logic_vector(3 downto 0); |
dbus_ack_i: in std_logic; |
dbus_adr_o: out std_logic_vector(31 downto 2); |
dbus_dat_o: out std_logic_vector(31 downto 0); |
dbus_dat_i: in std_logic_vector(31 downto 0); |
|
irq_i: in std_logic_vector(7 downto 0) |
); |
end entity; |
|
architecture rtl of lxp32c_top is |
|
signal lli_re: std_logic; |
signal lli_adr: std_logic_vector(29 downto 0); |
signal lli_dat: std_logic_vector(31 downto 0); |
signal lli_busy: std_logic; |
|
begin |
|
cpu_inst: entity work.lxp32_cpu(rtl) |
generic map( |
DBUS_RMW=>DBUS_RMW, |
DIVIDER_EN=>DIVIDER_EN, |
MUL_ARCH=>MUL_ARCH, |
START_ADDR=>START_ADDR |
) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
lli_re_o=>lli_re, |
lli_adr_o=>lli_adr, |
lli_dat_i=>lli_dat, |
lli_busy_i=>lli_busy, |
|
dbus_cyc_o=>dbus_cyc_o, |
dbus_stb_o=>dbus_stb_o, |
dbus_we_o=>dbus_we_o, |
dbus_sel_o=>dbus_sel_o, |
dbus_ack_i=>dbus_ack_i, |
dbus_adr_o=>dbus_adr_o, |
dbus_dat_o=>dbus_dat_o, |
dbus_dat_i=>dbus_dat_i, |
|
irq_i=>irq_i |
); |
|
icache_inst: entity work.lxp32_icache(rtl) |
generic map( |
BURST_SIZE=>IBUS_BURST_SIZE, |
PREFETCH_SIZE=>IBUS_PREFETCH_SIZE |
) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
lli_re_i=>lli_re, |
lli_adr_i=>lli_adr, |
lli_dat_o=>lli_dat, |
lli_busy_o=>lli_busy, |
|
wbm_cyc_o=>ibus_cyc_o, |
wbm_stb_o=>ibus_stb_o, |
wbm_cti_o=>ibus_cti_o, |
wbm_bte_o=>ibus_bte_o, |
wbm_ack_i=>ibus_ack_i, |
wbm_adr_o=>ibus_adr_o, |
wbm_dat_i=>ibus_dat_i |
); |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/run/xsim/.gitignore
0,0 → 1,11
/.Xil |
/xsim.dir |
webtalk* |
xelab* |
xsim* |
xvhdl* |
hs_err* |
vivado* |
*.ram |
*.stamp |
*.wdb |
/lxp32/tags/1.0/verify/lxp32/run/xsim/Makefile
0,0 → 1,48
include ../../src/make/sources.make |
|
ifeq ($(findstring Windows,$(OS)),) |
BAT= |
else |
BAT=.bat |
endif |
|
######################## |
# Phony targets |
######################## |
|
all: batch |
|
.PHONY: all compile batch gui clean |
|
compile: compile.stamp |
|
batch: compile.stamp |
xsim$(BAT) -R tb_sim |
|
gui: compile.stamp |
xsim$(BAT) -g -onfinish stop -onerror stop tb_sim |
|
clean: |
rm -rf .Xil |
rm -rf xsim.dir |
rm -f webtalk* |
rm -f xelab* |
rm -f xsim* |
rm -f xvhdl* |
rm -f hs_err* |
rm -f vivado* |
rm -f *.wdb |
rm -f $(FIRMWARE) |
rm -f compile.stamp |
|
######################## |
# Normal targets |
######################## |
|
compile.stamp: $(LXP32_RTL) $(PLATFORM_RTL) $(TB_SRC) $(FIRMWARE) |
xvhdl$(BAT) $(LXP32_RTL) $(PLATFORM_RTL) $(TB_SRC) |
xelab$(BAT) work.tb -s tb_sim -debug typical |
echo > compile.stamp |
|
%.ram: $(FW_SRC_DIR)/%.asm |
$(ASM) -f textio $^ -o $@ |
/lxp32/tags/1.0/verify/lxp32/run/ghdl/.gitignore
0,0 → 1,6
*.ram |
*.vcd |
*.cf |
*.o |
tb |
compile.stamp |
/lxp32/tags/1.0/verify/lxp32/run/ghdl/Makefile
0,0 → 1,51
include ../../src/make/sources.make |
|
GHDL_FLAGS=--std=93 |
|
WAVE_VCD=wave.vcd |
WAVE_OUT=wave.fst |
|
######################## |
# Phony targets |
######################## |
|
all: batch |
|
.PHONY: all compile batch gui clean |
|
.PRECIOUS: $(WAVE_OUT) |
|
compile: compile.stamp $(FIRMWARE) |
|
batch: compile.stamp $(FIRMWARE) |
ghdl -r $(GHDL_FLAGS) $(TB_MOD) |
|
gui: $(WAVE_OUT) |
gtkwave $(WAVE_OUT) |
|
clean: |
rm -f *.cf |
rm -f $(WAVE_VCD) |
rm -f $(WAVE_OUT) |
rm -f $(FIRMWARE) |
rm -f *.o |
rm -f $(TB_MOD) |
rm -f compile.stamp |
|
######################## |
# Normal targets |
######################## |
|
$(WAVE_OUT): $(WAVE_VCD) |
vcd2fst $^ $@ |
|
$(WAVE_VCD): compile.stamp $(FIRMWARE) |
ghdl -r $(GHDL_FLAGS) $(TB_MOD) --vcd=$(WAVE_VCD) |
|
compile.stamp: $(LXP32_RTL) $(PLATFORM_RTL) $(TB_SRC) |
ghdl -a $(GHDL_FLAGS) $(LXP32_RTL) $(PLATFORM_RTL) $(TB_SRC) |
ghdl -e $(GHDL_FLAGS) $(TB_MOD) |
echo > compile.stamp |
|
%.ram: $(FW_SRC_DIR)/%.asm |
$(ASM) -f textio $^ -o $@ |
/lxp32/tags/1.0/verify/lxp32/run/vsim/.gitignore
0,0 → 1,7
/work |
*.ram |
*.stamp |
*.wlf |
*.ini |
*.o |
transcript |
/lxp32/tags/1.0/verify/lxp32/run/vsim/Makefile
0,0 → 1,43
include ../../src/make/sources.make |
|
VCOMFLAGS=-93 |
VSIMFLAGS=-t 1ps |
|
######################## |
# Phony targets |
######################## |
|
all: batch |
|
.PHONY: all compile batch gui clean |
|
compile: compile.stamp |
|
batch: compile.stamp |
vsim $(VSIMFLAGS) -do "run -all; quit -f" -c work.$(TB_MOD) |
|
gui: compile.stamp |
vsim $(VSIMFLAGS) work.$(TB_MOD) |
|
clean: |
rm -rf work |
rm -f modelsim.ini |
rm -f transcript |
rm -f vsim.wlf |
rm -f $(FIRMWARE) |
rm -f compile.stamp |
|
######################## |
# Normal targets |
######################## |
|
compile.stamp: $(LXP32_RTL) $(PLATFORM_RTL) $(TB_SRC) $(FIRMWARE) | work |
vcom $(VCOMFLAGS) $(LXP32_RTL) $(PLATFORM_RTL) $(TB_SRC) |
echo > compile.stamp |
|
work: |
vlib work |
vmap work work |
|
%.ram: $(FW_SRC_DIR)/%.asm |
$(ASM) -f textio $^ -o $@ |
/lxp32/tags/1.0/verify/lxp32/src/platform/generic_dpram.vhd
0,0 → 1,142
--------------------------------------------------------------------- |
-- Generic FPGA memory block |
-- |
-- Copyright (c) 2015 by Alex I. Kuznetsov |
-- |
-- Portable description of a dual-port memory block with one write |
-- port. |
-- |
-- Parameters: |
-- * DATA_WIDTH: data port width |
-- * ADDR_WIDTH: address port width |
-- * SIZE: memory size |
-- * MODE: read/write synchronization mode for port A |
-- DONTCARE: choose the most efficient design |
-- WR_FIRST: feed written value to the output |
-- RD_FIRST: read old value |
-- NOCHANGE: don't change output during write |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity generic_dpram is |
generic( |
DATA_WIDTH: integer; |
ADDR_WIDTH: integer; |
SIZE: integer; |
MODE: string:="DONTCARE" |
); |
port( |
clka_i: in std_logic; |
cea_i: in std_logic; |
wea_i: in std_logic; |
addra_i: in std_logic_vector(ADDR_WIDTH-1 downto 0); |
da_i: in std_logic_vector(DATA_WIDTH-1 downto 0); |
da_o: out std_logic_vector(DATA_WIDTH-1 downto 0); |
|
clkb_i: in std_logic; |
ceb_i: in std_logic; |
addrb_i: in std_logic_vector(ADDR_WIDTH-1 downto 0); |
db_o: out std_logic_vector(DATA_WIDTH-1 downto 0) |
); |
end entity; |
|
architecture rtl of generic_dpram is |
|
type ram_type is array(SIZE-1 downto 0) of std_logic_vector(DATA_WIDTH-1 downto 0); |
signal ram: ram_type; |
|
attribute syn_ramstyle: string; |
attribute syn_ramstyle of ram: signal is "block_ram,no_rw_check"; |
attribute ram_style: string; -- for Xilinx |
attribute ram_style of ram: signal is "block"; |
|
begin |
|
-- Ensure that generics have valid values |
|
assert SIZE<=2**ADDR_WIDTH |
report "SIZE must be less or equal than 2^ADDR_WIDTH" |
severity failure; |
|
assert MODE="DONTCARE" or MODE="WR_FIRST" or MODE="RD_FIRST" or MODE="NOCHANGE" |
report "Unrecognized MODE value (DONTCARE, WR_FIRST, RD_FIRST or NOCHANGE expected)" |
severity failure; |
|
-- Port A (read/write) |
|
port_a_dont_care_gen: if MODE="DONTCARE" generate |
process (clka_i) is |
begin |
if rising_edge(clka_i) then |
if cea_i='1' then |
if wea_i='1' then |
ram(to_integer(unsigned(addra_i)))<=da_i; |
da_o<=(others=>'-'); |
else |
da_o<=ram(to_integer(to_01(unsigned(addra_i)))); |
end if; |
end if; |
end if; |
end process; |
end generate; |
|
port_a_write_first_gen: if MODE="WR_FIRST" generate |
process (clka_i) is |
begin |
if rising_edge(clka_i) then |
if cea_i='1' then |
if wea_i='1' then |
ram(to_integer(unsigned(addra_i)))<=da_i; |
da_o<=da_i; |
else |
da_o<=ram(to_integer(to_01(unsigned(addra_i)))); |
end if; |
end if; |
end if; |
end process; |
end generate; |
|
port_a_read_first_gen: if MODE="RD_FIRST" generate |
process (clka_i) is |
begin |
if rising_edge(clka_i) then |
if cea_i='1' then |
if wea_i='1' then |
ram(to_integer(unsigned(addra_i)))<=da_i; |
end if; |
da_o<=ram(to_integer(to_01(unsigned(addra_i)))); |
end if; |
end if; |
end process; |
end generate; |
|
port_a_no_change_gen: if MODE="NOCHANGE" generate |
process (clka_i) is |
begin |
if rising_edge(clka_i) then |
if cea_i='1' then |
if wea_i='1' then |
ram(to_integer(unsigned(addra_i)))<=da_i; |
else |
da_o<=ram(to_integer(to_01(unsigned(addra_i)))); |
end if; |
end if; |
end if; |
end process; |
end generate; |
|
-- Port B (read only) |
|
process (clkb_i) is |
begin |
if rising_edge(clkb_i) then |
if ceb_i='1' then |
db_o<=ram(to_integer(to_01(unsigned(addrb_i)))); |
end if; |
end if; |
end process; |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/platform/timer.vhd
0,0 → 1,98
--------------------------------------------------------------------- |
-- Timer |
-- |
-- Part of the LXP32 test platform |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- A simple programmable interval timer. |
-- |
-- Note: regardless of whether this description is synthesizable, |
-- it was designed exclusively for simulation purposes. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity timer is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
wbs_cyc_i: in std_logic; |
wbs_stb_i: in std_logic; |
wbs_we_i: in std_logic; |
wbs_sel_i: in std_logic_vector(3 downto 0); |
wbs_ack_o: out std_logic; |
wbs_adr_i: in std_logic_vector(27 downto 2); |
wbs_dat_i: in std_logic_vector(31 downto 0); |
wbs_dat_o: out std_logic_vector(31 downto 0); |
|
elapsed_o: out std_logic |
); |
end entity; |
|
architecture rtl of timer is |
|
signal pulses: unsigned(31 downto 0):=(others=>'0'); |
signal interval: unsigned(31 downto 0):=(others=>'0'); |
signal cnt: unsigned(31 downto 0):=(others=>'0'); |
signal elapsed: std_logic:='0'; |
|
begin |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
pulses<=(others=>'0'); |
interval<=(others=>'0'); |
cnt<=(others=>'0'); |
elapsed<='0'; |
else |
elapsed<='0'; |
if pulses/=X"00000000" or cnt/=X"00000000" then |
if cnt=X"00000000" then |
if pulses/=X"FFFFFFFF" then |
pulses<=pulses-1; |
end if; |
if pulses/=X"00000000" then |
cnt<=interval; |
end if; |
else |
cnt<=cnt-1; |
end if; |
if cnt=X"00000001" then |
elapsed<='1'; |
end if; |
end if; |
|
if wbs_cyc_i='1' and wbs_stb_i='1' and wbs_we_i='1' then |
for i in wbs_sel_i'range loop |
if wbs_sel_i(i)='1' then |
if wbs_adr_i="00"&X"000000" then |
pulses(i*8+7 downto i*8)<= |
unsigned(wbs_dat_i(i*8+7 downto i*8)); |
cnt<=(others=>'0'); |
end if; |
if wbs_adr_i="00"&X"000001" then |
interval(i*8+7 downto i*8)<= |
unsigned(wbs_dat_i(i*8+7 downto i*8)); |
cnt<=(others=>'0'); |
end if; |
end if; |
end loop; |
end if; |
end if; |
end if; |
end process; |
|
wbs_ack_o<=wbs_cyc_i and wbs_stb_i; |
wbs_dat_o<=std_logic_vector(pulses) when wbs_adr_i="00"&X"000000" else |
std_logic_vector(interval) when wbs_adr_i="00"&X"000001" else |
(others=>'-'); |
|
elapsed_o<=elapsed; |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/platform/coprocessor.vhd
0,0 → 1,97
--------------------------------------------------------------------- |
-- Coprocessor |
-- |
-- Part of the LXP32 test platform |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Performs a simple arithmetic operation, uses interrupt to wake |
-- up the CPU. |
-- |
-- Note: regardless of whether this description is synthesizable, |
-- it was designed exclusively for simulation purposes. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity coprocessor is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
wbs_cyc_i: in std_logic; |
wbs_stb_i: in std_logic; |
wbs_we_i: in std_logic; |
wbs_sel_i: in std_logic_vector(3 downto 0); |
wbs_ack_o: out std_logic; |
wbs_adr_i: in std_logic_vector(27 downto 2); |
wbs_dat_i: in std_logic_vector(31 downto 0); |
wbs_dat_o: out std_logic_vector(31 downto 0); |
|
irq_o: out std_logic |
); |
end entity; |
|
architecture rtl of coprocessor is |
|
signal value: unsigned(31 downto 0):=(others=>'0'); |
signal result: unsigned(31 downto 0):=(others=>'0'); |
signal cnt: integer range 0 to 5:=0; |
signal irq: std_logic:='0'; |
|
begin |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
value<=(others=>'0'); |
cnt<=0; |
irq<='0'; |
else |
if cnt>0 then |
cnt<=cnt-1; |
end if; |
|
if cnt=1 then |
irq<='1'; |
else |
irq<='0'; |
end if; |
|
if wbs_cyc_i='1' and wbs_stb_i='1' and wbs_we_i='1' then |
for i in wbs_sel_i'range loop |
if wbs_sel_i(i)='1' then |
if wbs_adr_i="00"&X"000000" then |
value(i*8+7 downto i*8)<= |
unsigned(wbs_dat_i(i*8+7 downto i*8)); |
cnt<=5; |
end if; |
end if; |
end loop; |
end if; |
end if; |
end if; |
end process; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
result<=(others=>'0'); |
else |
result<=shift_left(value,1)+value; |
end if; |
end if; |
end process; |
|
wbs_ack_o<=wbs_cyc_i and wbs_stb_i; |
wbs_dat_o<=std_logic_vector(value) when wbs_adr_i="00"&X"000000" else |
std_logic_vector(result) when wbs_adr_i="00"&X"000001" else |
(others=>'-'); |
|
irq_o<=irq; |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/platform/intercon.vhd
0,0 → 1,215
--------------------------------------------------------------------- |
-- Simple WISHBONE interconnect |
-- |
-- Generated by wigen at 02/16/16 06:15:08 |
-- |
-- Configuration: |
-- Number of masters: 2 |
-- Number of slaves: 4 |
-- Master address width: 32 |
-- Slave address width: 28 |
-- Port size: 32 |
-- Port granularity: 8 |
-- Entity name: intercon |
-- Pipelined arbiter: no |
-- Registered feedback: no |
-- Unsafe slave decoder: no |
-- |
-- Command line: |
-- wigen -e intercon 2 4 32 28 32 8 |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity intercon is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
s0_cyc_i: in std_logic; |
s0_stb_i: in std_logic; |
s0_we_i: in std_logic; |
s0_sel_i: in std_logic_vector(3 downto 0); |
s0_ack_o: out std_logic; |
s0_adr_i: in std_logic_vector(31 downto 2); |
s0_dat_i: in std_logic_vector(31 downto 0); |
s0_dat_o: out std_logic_vector(31 downto 0); |
|
s1_cyc_i: in std_logic; |
s1_stb_i: in std_logic; |
s1_we_i: in std_logic; |
s1_sel_i: in std_logic_vector(3 downto 0); |
s1_ack_o: out std_logic; |
s1_adr_i: in std_logic_vector(31 downto 2); |
s1_dat_i: in std_logic_vector(31 downto 0); |
s1_dat_o: out std_logic_vector(31 downto 0); |
|
m0_cyc_o: out std_logic; |
m0_stb_o: out std_logic; |
m0_we_o: out std_logic; |
m0_sel_o: out std_logic_vector(3 downto 0); |
m0_ack_i: in std_logic; |
m0_adr_o: out std_logic_vector(27 downto 2); |
m0_dat_o: out std_logic_vector(31 downto 0); |
m0_dat_i: in std_logic_vector(31 downto 0); |
|
m1_cyc_o: out std_logic; |
m1_stb_o: out std_logic; |
m1_we_o: out std_logic; |
m1_sel_o: out std_logic_vector(3 downto 0); |
m1_ack_i: in std_logic; |
m1_adr_o: out std_logic_vector(27 downto 2); |
m1_dat_o: out std_logic_vector(31 downto 0); |
m1_dat_i: in std_logic_vector(31 downto 0); |
|
m2_cyc_o: out std_logic; |
m2_stb_o: out std_logic; |
m2_we_o: out std_logic; |
m2_sel_o: out std_logic_vector(3 downto 0); |
m2_ack_i: in std_logic; |
m2_adr_o: out std_logic_vector(27 downto 2); |
m2_dat_o: out std_logic_vector(31 downto 0); |
m2_dat_i: in std_logic_vector(31 downto 0); |
|
m3_cyc_o: out std_logic; |
m3_stb_o: out std_logic; |
m3_we_o: out std_logic; |
m3_sel_o: out std_logic_vector(3 downto 0); |
m3_ack_i: in std_logic; |
m3_adr_o: out std_logic_vector(27 downto 2); |
m3_dat_o: out std_logic_vector(31 downto 0); |
m3_dat_i: in std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of intercon is |
|
signal request: std_logic_vector(1 downto 0); |
signal grant_next: std_logic_vector(1 downto 0); |
signal grant: std_logic_vector(1 downto 0); |
signal grant_reg: std_logic_vector(1 downto 0):=(others=>'0'); |
|
signal select_slave: std_logic_vector(4 downto 0); |
|
signal cyc_mux: std_logic; |
signal stb_mux: std_logic; |
signal we_mux: std_logic; |
signal sel_mux: std_logic_vector(3 downto 0); |
signal adr_mux: std_logic_vector(31 downto 2); |
signal wdata_mux: std_logic_vector(31 downto 0); |
|
signal ack_mux: std_logic; |
signal rdata_mux: std_logic_vector(31 downto 0); |
|
begin |
|
-- ARBITER |
-- Selects the active master. Masters with lower port numbers |
-- have higher priority. Ongoing cycles are not interrupted. |
|
request<=s1_cyc_i&s0_cyc_i; |
|
grant_next<="01" when request(0)='1' else |
"10" when request(1)='1' else |
(others=>'0'); |
|
grant<=grant_reg when (request and grant_reg)/="00" else grant_next; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
grant_reg<=(others=>'0'); |
else |
grant_reg<=grant; |
end if; |
end if; |
end process; |
|
-- MASTER->SLAVE MUX |
|
cyc_mux<=(s0_cyc_i and grant(0)) or |
(s1_cyc_i and grant(1)); |
|
stb_mux<=(s0_stb_i and grant(0)) or |
(s1_stb_i and grant(1)); |
|
we_mux<=(s0_we_i and grant(0)) or |
(s1_we_i and grant(1)); |
|
sel_mux_gen: for i in sel_mux'range generate |
sel_mux(i)<=(s0_sel_i(i) and grant(0)) or |
(s1_sel_i(i) and grant(1)); |
end generate; |
|
adr_mux_gen: for i in adr_mux'range generate |
adr_mux(i)<=(s0_adr_i(i) and grant(0)) or |
(s1_adr_i(i) and grant(1)); |
end generate; |
|
wdata_mux_gen: for i in wdata_mux'range generate |
wdata_mux(i)<=(s0_dat_i(i) and grant(0)) or |
(s1_dat_i(i) and grant(1)); |
end generate; |
|
-- MASTER->SLAVE DEMUX |
|
select_slave<="00001" when adr_mux(31 downto 28)="0000" else |
"00010" when adr_mux(31 downto 28)="0001" else |
"00100" when adr_mux(31 downto 28)="0010" else |
"01000" when adr_mux(31 downto 28)="0011" else |
"10000"; -- fallback slave |
|
m0_cyc_o<=cyc_mux and select_slave(0); |
m0_stb_o<=stb_mux and select_slave(0); |
m0_we_o<=we_mux; |
m0_sel_o<=sel_mux; |
m0_adr_o<=adr_mux(m0_adr_o'range); |
m0_dat_o<=wdata_mux; |
|
m1_cyc_o<=cyc_mux and select_slave(1); |
m1_stb_o<=stb_mux and select_slave(1); |
m1_we_o<=we_mux; |
m1_sel_o<=sel_mux; |
m1_adr_o<=adr_mux(m1_adr_o'range); |
m1_dat_o<=wdata_mux; |
|
m2_cyc_o<=cyc_mux and select_slave(2); |
m2_stb_o<=stb_mux and select_slave(2); |
m2_we_o<=we_mux; |
m2_sel_o<=sel_mux; |
m2_adr_o<=adr_mux(m2_adr_o'range); |
m2_dat_o<=wdata_mux; |
|
m3_cyc_o<=cyc_mux and select_slave(3); |
m3_stb_o<=stb_mux and select_slave(3); |
m3_we_o<=we_mux; |
m3_sel_o<=sel_mux; |
m3_adr_o<=adr_mux(m3_adr_o'range); |
m3_dat_o<=wdata_mux; |
|
-- SLAVE->MASTER MUX |
|
ack_mux<=(m0_ack_i and select_slave(0)) or |
(m1_ack_i and select_slave(1)) or |
(m2_ack_i and select_slave(2)) or |
(m3_ack_i and select_slave(3)) or |
(cyc_mux and stb_mux and select_slave(4)); -- fallback slave |
|
rdata_mux_gen: for i in rdata_mux'range generate |
rdata_mux(i)<=(m0_dat_i(i) and select_slave(0)) or |
(m1_dat_i(i) and select_slave(1)) or |
(m2_dat_i(i) and select_slave(2)) or |
(m3_dat_i(i) and select_slave(3)); |
end generate; |
|
-- SLAVE->MASTER DEMUX |
|
s0_ack_o<=ack_mux and grant(0); |
s0_dat_o<=rdata_mux; |
|
s1_ack_o<=ack_mux and grant(1); |
s1_dat_o<=rdata_mux; |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/platform/ibus_adapter.vhd
0,0 → 1,101
--------------------------------------------------------------------- |
-- IBUS adapter |
-- |
-- Part of the LXP32 test platform |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Converts the Low Latency Interface to WISHBONE registered |
-- feedback protocol. |
-- |
-- Note: regardless of whether this description is synthesizable, |
-- it was designed exclusively for simulation purposes. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
entity ibus_adapter is |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
ibus_cyc_i: in std_logic; |
ibus_stb_i: in std_logic; |
ibus_cti_i: in std_logic_vector(2 downto 0); |
ibus_bte_i: in std_logic_vector(1 downto 0); |
ibus_ack_o: out std_logic; |
ibus_adr_i: in std_logic_vector(29 downto 0); |
ibus_dat_o: out std_logic_vector(31 downto 0); |
|
lli_re_o: out std_logic; |
lli_adr_o: out std_logic_vector(29 downto 0); |
lli_dat_i: in std_logic_vector(31 downto 0); |
lli_busy_i: in std_logic |
); |
end entity; |
|
architecture rtl of ibus_adapter is |
|
constant burst_delay: integer:=5; |
signal burst_delay_cnt: integer:=0; |
signal delay_burst: std_logic; |
|
signal re: std_logic; |
signal requested: std_logic:='0'; |
signal adr: unsigned(29 downto 0); |
signal ack: std_logic; |
|
begin |
|
-- Insert burst delay |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
burst_delay_cnt<=0; |
elsif ibus_cyc_i='0' then |
burst_delay_cnt<=burst_delay; |
elsif burst_delay_cnt/=0 then |
burst_delay_cnt<=burst_delay_cnt-1; |
end if; |
end if; |
end process; |
|
delay_burst<='1' when burst_delay_cnt/=0 else '0'; |
|
-- Generate ACK signal |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
requested<='0'; |
elsif lli_busy_i='0' then |
requested<=re; |
end if; |
end if; |
end process; |
|
ack<=requested and not lli_busy_i; |
|
-- Generate LLI signals |
|
re<=(ibus_cyc_i and ibus_stb_i and not delay_burst) when ack='0' or |
(ibus_cti_i="010" and ibus_bte_i="00") else '0'; |
|
adr<=unsigned(ibus_adr_i) when re='1' and ack='0' else |
unsigned(ibus_adr_i)+1 when re='1' and ack='1' else |
(others=>'-'); |
|
lli_re_o<=re; |
lli_adr_o<=std_logic_vector(adr); |
|
-- Generate IBUS signals |
|
ibus_ack_o<=ack; |
ibus_dat_o<=lli_dat_i when ack='1' else (others=>'-'); |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/platform/dbus_monitor.vhd
0,0 → 1,95
--------------------------------------------------------------------- |
-- DBUS monitor |
-- |
-- Part of the LXP32 test platform |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Monitors LXP32 data bus transactions, optionally throttles them. |
-- |
-- Note: regardless of whether this description is synthesizable, |
-- it was designed exclusively for simulation purposes. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity dbus_monitor is |
generic( |
THROTTLE: boolean |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
wbs_cyc_i: in std_logic; |
wbs_stb_i: in std_logic; |
wbs_we_i: in std_logic; |
wbs_sel_i: in std_logic_vector(3 downto 0); |
wbs_ack_o: out std_logic; |
wbs_adr_i: in std_logic_vector(31 downto 2); |
wbs_dat_i: in std_logic_vector(31 downto 0); |
wbs_dat_o: out std_logic_vector(31 downto 0); |
|
wbm_cyc_o: out std_logic; |
wbm_stb_o: out std_logic; |
wbm_we_o: out std_logic; |
wbm_sel_o: out std_logic_vector(3 downto 0); |
wbm_ack_i: in std_logic; |
wbm_adr_o: out std_logic_vector(31 downto 2); |
wbm_dat_o: out std_logic_vector(31 downto 0); |
wbm_dat_i: in std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of dbus_monitor is |
|
signal prbs: std_logic; |
signal cycle: std_logic:='0'; |
|
begin |
|
-- Manage throttling |
|
gen_throttling: if THROTTLE generate |
throttle_inst: entity work.scrambler(rtl) |
generic map(TAP1=>6,TAP2=>7) |
port map(clk_i=>clk_i,rst_i=>rst_i,ce_i=>'1',d_o=>prbs); |
end generate; |
|
gen_no_throttling: if not THROTTLE generate |
prbs<='0'; |
end generate; |
|
-- CPU interface |
|
wbs_ack_o<=wbm_ack_i; |
wbs_dat_o<=wbm_dat_i when wbm_ack_i='1' else (others=>'-'); |
|
-- Interconnect interface |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
cycle<='0'; |
elsif prbs='0' and wbs_cyc_i='1' then |
cycle<='1'; |
elsif wbs_cyc_i='0' then |
cycle<='0'; |
end if; |
end if; |
end process; |
|
wbm_cyc_o<=wbs_cyc_i and (not prbs or cycle); |
wbm_stb_o<=wbs_stb_i and (not prbs or cycle); |
wbm_we_o<=wbs_we_i; |
wbm_sel_o<=wbs_sel_i; |
wbm_adr_o<=wbs_adr_i; |
wbm_dat_o<=wbs_dat_i; |
|
assert not rising_edge(clk_i) or wbm_ack_i='0' or (wbs_cyc_i and (not prbs or cycle))='1' |
report "DBUS error: ACK asserted without CYC" |
severity failure; |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/platform/platform.vhd
0,0 → 1,354
--------------------------------------------------------------------- |
-- LXP32 platform top-level design unit |
-- |
-- Part of the LXP32 test platform |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- A SoC-like simulation platform for the LXP32 CPU, containing |
-- a few peripherals such as program RAM, timer and coprocessor. |
-- |
-- Note: regardless of whether this description is synthesizable, |
-- it was designed exclusively for simulation purposes. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity platform is |
generic( |
MODEL_LXP32C: boolean; |
THROTTLE_DBUS: boolean; |
THROTTLE_IBUS: boolean |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
cpu_rst_i: in std_logic; |
|
wbm_cyc_o: out std_logic; |
wbm_stb_o: out std_logic; |
wbm_we_o: out std_logic; |
wbm_sel_o: out std_logic_vector(3 downto 0); |
wbm_ack_i: in std_logic; |
wbm_adr_o: out std_logic_vector(27 downto 2); |
wbm_dat_o: out std_logic_vector(31 downto 0); |
wbm_dat_i: in std_logic_vector(31 downto 0); |
|
wbs_cyc_i: in std_logic; |
wbs_stb_i: in std_logic; |
wbs_we_i: in std_logic; |
wbs_sel_i: in std_logic_vector(3 downto 0); |
wbs_ack_o: out std_logic; |
wbs_adr_i: in std_logic_vector(31 downto 2); |
wbs_dat_i: in std_logic_vector(31 downto 0); |
wbs_dat_o: out std_logic_vector(31 downto 0); |
|
gp_io: inout std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture rtl of platform is |
|
type wbm_type is record |
cyc: std_logic; |
stb: std_logic; |
we: std_logic; |
sel: std_logic_vector(3 downto 0); |
ack: std_logic; |
adr: std_logic_vector(31 downto 2); |
wdata: std_logic_vector(31 downto 0); |
rdata: std_logic_vector(31 downto 0); |
end record; |
|
type wbs_type is record |
cyc: std_logic; |
stb: std_logic; |
we: std_logic; |
sel: std_logic_vector(3 downto 0); |
ack: std_logic; |
adr: std_logic_vector(27 downto 2); |
wdata: std_logic_vector(31 downto 0); |
rdata: std_logic_vector(31 downto 0); |
end record; |
|
type ibus_type is record |
cyc: std_logic; |
stb: std_logic; |
cti: std_logic_vector(2 downto 0); |
bte: std_logic_vector(1 downto 0); |
ack: std_logic; |
adr: std_logic_vector(29 downto 0); |
dat: std_logic_vector(31 downto 0); |
end record; |
|
signal cpu_rst: std_logic; |
signal cpu_irq: std_logic_vector(7 downto 0); |
signal cpu_dbus: wbm_type; |
signal cpu_ibus: ibus_type; |
|
signal lli_re: std_logic; |
signal lli_adr: std_logic_vector(29 downto 0); |
signal lli_dat: std_logic_vector(31 downto 0); |
signal lli_busy: std_logic; |
|
signal monitor_dbus: wbm_type; |
|
signal ram_wb: wbs_type; |
|
signal timer_wb: wbs_type; |
signal timer_elapsed: std_logic; |
|
signal coprocessor_wb: wbs_type; |
signal coprocessor_irq: std_logic; |
|
begin |
|
-- Interconnect |
|
intercon_inst: entity work.intercon(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
s0_cyc_i=>wbs_cyc_i, |
s0_stb_i=>wbs_stb_i, |
s0_we_i=>wbs_we_i, |
s0_sel_i=>wbs_sel_i, |
s0_ack_o=>wbs_ack_o, |
s0_adr_i=>wbs_adr_i, |
s0_dat_i=>wbs_dat_i, |
s0_dat_o=>wbs_dat_o, |
|
s1_cyc_i=>monitor_dbus.cyc, |
s1_stb_i=>monitor_dbus.stb, |
s1_we_i=>monitor_dbus.we, |
s1_sel_i=>monitor_dbus.sel, |
s1_ack_o=>monitor_dbus.ack, |
s1_adr_i=>monitor_dbus.adr, |
s1_dat_i=>monitor_dbus.wdata, |
s1_dat_o=>monitor_dbus.rdata, |
|
m0_cyc_o=>ram_wb.cyc, |
m0_stb_o=>ram_wb.stb, |
m0_we_o=>ram_wb.we, |
m0_sel_o=>ram_wb.sel, |
m0_ack_i=>ram_wb.ack, |
m0_adr_o=>ram_wb.adr, |
m0_dat_o=>ram_wb.wdata, |
m0_dat_i=>ram_wb.rdata, |
|
m1_cyc_o=>wbm_cyc_o, |
m1_stb_o=>wbm_stb_o, |
m1_we_o=>wbm_we_o, |
m1_sel_o=>wbm_sel_o, |
m1_ack_i=>wbm_ack_i, |
m1_adr_o=>wbm_adr_o, |
m1_dat_o=>wbm_dat_o, |
m1_dat_i=>wbm_dat_i, |
|
m2_cyc_o=>timer_wb.cyc, |
m2_stb_o=>timer_wb.stb, |
m2_we_o=>timer_wb.we, |
m2_sel_o=>timer_wb.sel, |
m2_ack_i=>timer_wb.ack, |
m2_adr_o=>timer_wb.adr, |
m2_dat_o=>timer_wb.wdata, |
m2_dat_i=>timer_wb.rdata, |
|
m3_cyc_o=>coprocessor_wb.cyc, |
m3_stb_o=>coprocessor_wb.stb, |
m3_we_o=>coprocessor_wb.we, |
m3_sel_o=>coprocessor_wb.sel, |
m3_ack_i=>coprocessor_wb.ack, |
m3_adr_o=>coprocessor_wb.adr, |
m3_dat_o=>coprocessor_wb.wdata, |
m3_dat_i=>coprocessor_wb.rdata |
); |
|
-- CPU |
|
cpu_rst<=cpu_rst_i or rst_i; |
|
-- Note: we connect the timer IRQ to 2 CPU channels to test |
-- handling of simultaneously arriving interrupt requests. |
|
cpu_irq<="00000"&coprocessor_irq&timer_elapsed&timer_elapsed; |
|
gen_lxp32u: if not MODEL_LXP32C generate |
lxp32u_top_inst: entity work.lxp32u_top(rtl) |
generic map( |
DBUS_RMW=>false, |
DIVIDER_EN=>true, |
MUL_ARCH=>"dsp", |
START_ADDR=>(others=>'0') |
) |
port map( |
clk_i=>clk_i, |
rst_i=>cpu_rst, |
|
lli_re_o=>lli_re, |
lli_adr_o=>lli_adr, |
lli_dat_i=>lli_dat, |
lli_busy_i=>lli_busy, |
|
dbus_cyc_o=>cpu_dbus.cyc, |
dbus_stb_o=>cpu_dbus.stb, |
dbus_we_o=>cpu_dbus.we, |
dbus_sel_o=>cpu_dbus.sel, |
dbus_ack_i=>cpu_dbus.ack, |
dbus_adr_o=>cpu_dbus.adr, |
dbus_dat_o=>cpu_dbus.wdata, |
dbus_dat_i=>cpu_dbus.rdata, |
|
irq_i=>cpu_irq |
); |
end generate; |
|
gen_lxp32c: if MODEL_LXP32C generate |
lxp32c_top_inst: entity work.lxp32c_top(rtl) |
generic map( |
DBUS_RMW=>false, |
DIVIDER_EN=>true, |
IBUS_BURST_SIZE=>16, |
IBUS_PREFETCH_SIZE=>32, |
MUL_ARCH=>"dsp", |
START_ADDR=>(others=>'0') |
) |
port map( |
clk_i=>clk_i, |
rst_i=>cpu_rst, |
|
ibus_cyc_o=>cpu_ibus.cyc, |
ibus_stb_o=>cpu_ibus.stb, |
ibus_cti_o=>cpu_ibus.cti, |
ibus_bte_o=>cpu_ibus.bte, |
ibus_ack_i=>cpu_ibus.ack, |
ibus_adr_o=>cpu_ibus.adr, |
ibus_dat_i=>cpu_ibus.dat, |
|
dbus_cyc_o=>cpu_dbus.cyc, |
dbus_stb_o=>cpu_dbus.stb, |
dbus_we_o=>cpu_dbus.we, |
dbus_sel_o=>cpu_dbus.sel, |
dbus_ack_i=>cpu_dbus.ack, |
dbus_adr_o=>cpu_dbus.adr, |
dbus_dat_o=>cpu_dbus.wdata, |
dbus_dat_i=>cpu_dbus.rdata, |
|
irq_i=>cpu_irq |
); |
|
ibus_adapter_inst: entity work.ibus_adapter(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
ibus_cyc_i=>cpu_ibus.cyc, |
ibus_stb_i=>cpu_ibus.stb, |
ibus_cti_i=>cpu_ibus.cti, |
ibus_bte_i=>cpu_ibus.bte, |
ibus_ack_o=>cpu_ibus.ack, |
ibus_adr_i=>cpu_ibus.adr, |
ibus_dat_o=>cpu_ibus.dat, |
|
lli_re_o=>lli_re, |
lli_adr_o=>lli_adr, |
lli_dat_i=>lli_dat, |
lli_busy_i=>lli_busy |
); |
end generate; |
|
-- DBUS monitor |
|
dbus_monitor_inst: entity work.dbus_monitor(rtl) |
generic map( |
THROTTLE=>THROTTLE_DBUS |
) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
wbs_cyc_i=>cpu_dbus.cyc, |
wbs_stb_i=>cpu_dbus.stb, |
wbs_we_i=>cpu_dbus.we, |
wbs_sel_i=>cpu_dbus.sel, |
wbs_ack_o=>cpu_dbus.ack, |
wbs_adr_i=>cpu_dbus.adr, |
wbs_dat_i=>cpu_dbus.wdata, |
wbs_dat_o=>cpu_dbus.rdata, |
|
wbm_cyc_o=>monitor_dbus.cyc, |
wbm_stb_o=>monitor_dbus.stb, |
wbm_we_o=>monitor_dbus.we, |
wbm_sel_o=>monitor_dbus.sel, |
wbm_ack_i=>monitor_dbus.ack, |
wbm_adr_o=>monitor_dbus.adr, |
wbm_dat_o=>monitor_dbus.wdata, |
wbm_dat_i=>monitor_dbus.rdata |
); |
|
-- Program RAM |
|
program_ram_inst: entity work.program_ram(rtl) |
generic map( |
THROTTLE=>THROTTLE_IBUS |
) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
wbs_cyc_i=>ram_wb.cyc, |
wbs_stb_i=>ram_wb.stb, |
wbs_we_i=>ram_wb.we, |
wbs_sel_i=>ram_wb.sel, |
wbs_ack_o=>ram_wb.ack, |
wbs_adr_i=>ram_wb.adr, |
wbs_dat_i=>ram_wb.wdata, |
wbs_dat_o=>ram_wb.rdata, |
|
lli_re_i=>lli_re, |
lli_adr_i=>lli_adr, |
lli_dat_o=>lli_dat, |
lli_busy_o=>lli_busy |
); |
|
-- Timer |
|
timer_inst: entity work.timer(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
wbs_cyc_i=>timer_wb.cyc, |
wbs_stb_i=>timer_wb.stb, |
wbs_we_i=>timer_wb.we, |
wbs_sel_i=>timer_wb.sel, |
wbs_ack_o=>timer_wb.ack, |
wbs_adr_i=>timer_wb.adr, |
wbs_dat_i=>timer_wb.wdata, |
wbs_dat_o=>timer_wb.rdata, |
|
elapsed_o=>timer_elapsed |
); |
|
-- Coprocessor |
|
coprocessor_inst: entity work.coprocessor(rtl) |
port map( |
clk_i=>clk_i, |
rst_i=>rst_i, |
|
wbs_cyc_i=>coprocessor_wb.cyc, |
wbs_stb_i=>coprocessor_wb.stb, |
wbs_we_i=>coprocessor_wb.we, |
wbs_sel_i=>coprocessor_wb.sel, |
wbs_ack_o=>coprocessor_wb.ack, |
wbs_adr_i=>coprocessor_wb.adr, |
wbs_dat_i=>coprocessor_wb.wdata, |
wbs_dat_o=>coprocessor_wb.rdata, |
|
irq_o=>coprocessor_irq |
); |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/platform/program_ram.vhd
0,0 → 1,133
--------------------------------------------------------------------- |
-- Program RAM |
-- |
-- Part of the LXP32 test platform |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Program RAM for the LXP32 test platform. Has two interfaces: |
-- WISHBONE (for data access) and LLI (for LXP32 instruction bus). |
-- Optionally performs throttling. |
-- |
-- Note: regardless of whether this description is synthesizable, |
-- it was designed exclusively for simulation purposes. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity program_ram is |
generic( |
THROTTLE: boolean |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
wbs_cyc_i: in std_logic; |
wbs_stb_i: in std_logic; |
wbs_we_i: in std_logic; |
wbs_sel_i: in std_logic_vector(3 downto 0); |
wbs_ack_o: out std_logic; |
wbs_adr_i: in std_logic_vector(27 downto 2); |
wbs_dat_i: in std_logic_vector(31 downto 0); |
wbs_dat_o: out std_logic_vector(31 downto 0); |
|
lli_re_i: in std_logic; |
lli_adr_i: in std_logic_vector(29 downto 0); |
lli_dat_o: out std_logic_vector(31 downto 0); |
lli_busy_o: out std_logic |
); |
end entity; |
|
architecture rtl of program_ram is |
|
signal ram_a_we: std_logic_vector(3 downto 0); |
signal ram_a_rdata: std_logic_vector(31 downto 0); |
|
signal ram_b_re: std_logic; |
signal ram_b_rdata: std_logic_vector(31 downto 0); |
|
signal ack_write: std_logic; |
signal ack_read: std_logic; |
|
signal prbs: std_logic; |
signal lli_busy: std_logic:='0'; |
|
begin |
|
-- The total memory size is 16384 words, i.e. 64K bytes |
|
gen_dprams: for i in 3 downto 0 generate |
generic_dpram_inst: entity work.generic_dpram(rtl) |
generic map( |
DATA_WIDTH=>8, |
ADDR_WIDTH=>14, |
SIZE=>16384, |
MODE=>"DONTCARE" |
) |
port map( |
clka_i=>clk_i, |
cea_i=>'1', |
wea_i=>ram_a_we(i), |
addra_i=>wbs_adr_i(15 downto 2), |
da_i=>wbs_dat_i(i*8+7 downto i*8), |
da_o=>ram_a_rdata(i*8+7 downto i*8), |
|
clkb_i=>clk_i, |
ceb_i=>ram_b_re, |
addrb_i=>lli_adr_i(13 downto 0), |
db_o=>ram_b_rdata(i*8+7 downto i*8) |
); |
end generate; |
|
-- WISHBONE interface |
|
gen_ram_a_we: for i in 3 downto 0 generate |
ram_a_we(i)<='1' when wbs_cyc_i='1' and wbs_stb_i='1' and wbs_we_i='1' |
and wbs_sel_i(i)='1' and wbs_adr_i(27 downto 16)="000000000000" else '0'; |
end generate; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
ack_read<=wbs_cyc_i and wbs_stb_i and not wbs_we_i; |
end if; |
end process; |
|
ack_write<=wbs_cyc_i and wbs_stb_i and wbs_we_i; |
|
wbs_ack_o<=ack_read or ack_write; |
wbs_dat_o<=ram_a_rdata; |
|
-- Low Latency Interface (with optional pseudo-random throttling) |
|
gen_throttling: if THROTTLE generate |
throttle_inst: entity work.scrambler(rtl) |
generic map(TAP1=>9,TAP2=>11) |
port map(clk_i=>clk_i,rst_i=>rst_i,ce_i=>'1',d_o=>prbs); |
end generate; |
|
gen_no_throttling: if not THROTTLE generate |
prbs<='0'; |
end generate; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
lli_busy<='0'; |
elsif prbs='1' and lli_re_i='1' then |
lli_busy<='1'; |
elsif prbs='0' then |
lli_busy<='0'; |
end if; |
end if; |
end process; |
|
ram_b_re<=lli_re_i and not lli_busy; |
|
lli_busy_o<=lli_busy; |
lli_dat_o<=ram_b_rdata when lli_busy='0' else (others=>'-'); |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/platform/scrambler.vhd
0,0 → 1,54
--------------------------------------------------------------------- |
-- Scrambler |
-- |
-- Part of the LXP32 test platform |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Generates a pseudo-random binary sequence using a Linear-Feedback |
-- Shift Register (LFSR). |
-- |
-- In order to generate a maximum-length sequence, 1+x^TAP1+x^TAP2 |
-- must be a primitive polynomial. Typical polynomials include: |
-- (6,7), (9,11), (14,15). |
-- |
-- Note: regardless of whether this description is synthesizable, |
-- it was designed exclusively for simulation purposes. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity scrambler is |
generic( |
TAP1: integer; |
TAP2: integer |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
ce_i: in std_logic; |
d_o: out std_logic |
); |
end entity; |
|
architecture rtl of scrambler is |
|
signal reg: std_logic_vector(TAP2 downto 1):=(others=>'1'); |
|
begin |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
reg<=(others=>'1'); |
elsif ce_i='1' then |
reg<=reg(TAP2-1 downto 1)&(reg(TAP2) xor reg(TAP1)); |
end if; |
end if; |
end process; |
|
d_o<=reg(1); |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/tb/tb_pkg_body.vhd
0,0 → 1,100
--------------------------------------------------------------------- |
-- LXP32 testbench package body |
-- |
-- Part of the LXP32 testbench |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Auxiliary package body for the LXP32 testbench |
--------------------------------------------------------------------- |
|
use std.textio.all; |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
use work.common_pkg.all; |
|
package body tb_pkg is |
procedure load_ram( |
filename: string; |
signal clk: in std_logic; |
signal soc_in: out soc_wbs_in_type; |
signal soc_out: in soc_wbs_out_type |
) is |
file f: text open read_mode is filename; |
variable i: integer:=0; |
variable l: line; |
variable v: bit_vector(31 downto 0); |
begin |
wait until rising_edge(clk); |
|
report "Loading program RAM from """&filename&""""; |
|
while not endfile(f) loop |
readline(f,l); |
read(l,v); |
|
assert i<c_max_program_size report "Error: program size is too large" severity failure; |
|
soc_in.cyc<='1'; |
soc_in.stb<='1'; |
soc_in.we<='1'; |
soc_in.sel<=(others=>'1'); |
soc_in.adr<=std_logic_vector(to_unsigned(i,30)); |
soc_in.dat<=to_stdlogicvector(v); |
|
wait until rising_edge(clk) and soc_out.ack='1'; |
|
i:=i+1; |
end loop; |
|
report integer'image(i)&" words loaded from """&filename&""""; |
|
soc_in.cyc<='0'; |
soc_in.stb<='0'; |
|
wait until rising_edge(clk); |
end procedure; |
|
procedure run_test( |
filename: string; |
signal clk: in std_logic; |
signal globals: out soc_globals_type; |
signal soc_in: out soc_wbs_in_type; |
signal soc_out: in soc_wbs_out_type; |
signal result: in monitor_out_type |
) is |
begin |
-- Assert SoC and CPU resets |
wait until rising_edge(clk); |
globals.rst_i<='1'; |
globals.cpu_rst_i<='1'; |
wait until rising_edge(clk); |
|
-- Deassert SoC reset, leave CPU in reset state for now |
globals.rst_i<='0'; |
wait until rising_edge(clk); |
|
-- Load RAM |
load_ram(filename,clk,soc_in,soc_out); |
|
-- Deassert CPU reset |
globals.cpu_rst_i<='0'; |
|
while result.valid/='1' loop |
wait until rising_edge(clk); |
end loop; |
|
-- Analyze result |
|
if result.data=X"00000001" then |
report "TEST """&filename&""" RESULT: SUCCESS (return code 0x"& |
hex_string(result.data)&")"; |
else |
report "TEST """&filename&""" RESULT: FAILURE (return code 0x"& |
hex_string(result.data)&")" severity failure; |
end if; |
end procedure; |
end package body; |
/lxp32/tags/1.0/verify/lxp32/src/tb/tb.vhd
0,0 → 1,137
--------------------------------------------------------------------- |
-- LXP32 verification environment (self-checking testbench) |
-- |
-- Part of the LXP32 testbench |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Simulates LXP32 test platform, verifies results. |
-- |
-- Parameters: |
-- MODEL_LXP32C: when true, simulates LXP32C variant (with |
-- instruction cache), otherwise LXP32U |
-- TEST_CASE: If non-empty, selects a test case to run. |
-- If empty, all tests are executed. |
-- THROTTLE_IBUS: perform pseudo-random instruction bus |
-- throttling |
-- THROTTLE_DBUS: perform pseudo-random data bus throttling |
-- VERBOSE: report everything that is written to the |
-- test monitor address space |
--------------------------------------------------------------------- |
|
use std.textio.all; |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
use work.tb_pkg.all; |
|
entity tb is |
generic( |
MODEL_LXP32C: boolean:=true; |
TEST_CASE: string:=""; |
THROTTLE_DBUS: boolean:=true; |
THROTTLE_IBUS: boolean:=true; |
VERBOSE: boolean:=false |
); |
end entity; |
|
architecture testbench of tb is |
|
signal clk: std_logic:='0'; |
|
signal globals: soc_globals_type:=(others=>'1'); |
signal soc_wbs_in: soc_wbs_in_type; |
signal soc_wbs_out: soc_wbs_out_type; |
signal soc_wbm_in: soc_wbm_in_type; |
signal soc_wbm_out: soc_wbm_out_type; |
|
signal monitor_out: monitor_out_type; |
|
signal finish: std_logic:='0'; |
|
begin |
|
dut: entity work.platform(rtl) |
generic map( |
MODEL_LXP32C=>MODEL_LXP32C, |
THROTTLE_DBUS=>THROTTLE_DBUS, |
THROTTLE_IBUS=>THROTTLE_IBUS |
) |
port map( |
clk_i=>clk, |
rst_i=>globals.rst_i, |
cpu_rst_i=>globals.cpu_rst_i, |
|
wbm_cyc_o=>soc_wbm_out.cyc, |
wbm_stb_o=>soc_wbm_out.stb, |
wbm_we_o=>soc_wbm_out.we, |
wbm_sel_o=>soc_wbm_out.sel, |
wbm_ack_i=>soc_wbm_in.ack, |
wbm_adr_o=>soc_wbm_out.adr, |
wbm_dat_o=>soc_wbm_out.dat, |
wbm_dat_i=>soc_wbm_in.dat, |
|
wbs_cyc_i=>soc_wbs_in.cyc, |
wbs_stb_i=>soc_wbs_in.stb, |
wbs_we_i=>soc_wbs_in.we, |
wbs_sel_i=>soc_wbs_in.sel, |
wbs_ack_o=>soc_wbs_out.ack, |
wbs_adr_i=>soc_wbs_in.adr, |
wbs_dat_i=>soc_wbs_in.dat, |
wbs_dat_o=>soc_wbs_out.dat |
); |
|
monitor_inst: entity work.monitor(sim) |
generic map( |
VERBOSE=>VERBOSE |
) |
port map( |
clk_i=>clk, |
rst_i=>globals.rst_i, |
|
wbs_cyc_i=>soc_wbm_out.cyc, |
wbs_stb_i=>soc_wbm_out.stb, |
wbs_we_i=>soc_wbm_out.we, |
wbs_sel_i=>soc_wbm_out.sel, |
wbs_ack_o=>soc_wbm_in.ack, |
wbs_adr_i=>soc_wbm_out.adr, |
wbs_dat_i=>soc_wbm_out.dat, |
wbs_dat_o=>soc_wbm_in.dat, |
|
finished_o=>monitor_out.valid, |
result_o=>monitor_out.data |
); |
|
clk<=not clk and not finish after 5 ns; |
|
process is |
begin |
if TEST_CASE'length=0 then |
run_test("test001.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test002.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test003.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test004.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test005.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test006.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test007.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test008.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test009.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test010.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test011.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test012.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test013.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test014.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test015.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
run_test("test016.ram",clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
else |
run_test(TEST_CASE,clk,globals,soc_wbs_in,soc_wbs_out,monitor_out); |
end if; |
|
report "ALL TESTS WERE COMPLETED SUCCESSFULLY"; |
finish<='1'; |
wait; |
end process; |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/tb/tb_pkg.vhd
0,0 → 1,70
--------------------------------------------------------------------- |
-- LXP32 testbench package |
-- |
-- Part of the LXP32 testbench |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Auxiliary package declaration for the LXP32 testbench |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
package tb_pkg is |
constant c_max_program_size: integer:=8192; |
|
type soc_globals_type is record |
rst_i: std_logic; |
cpu_rst_i: std_logic; |
end record; |
|
type soc_wbs_in_type is record |
cyc: std_logic; |
stb: std_logic; |
we: std_logic; |
sel: std_logic_vector(3 downto 0); |
adr: std_logic_vector(31 downto 2); |
dat: std_logic_vector(31 downto 0); |
end record; |
|
type soc_wbs_out_type is record |
ack: std_logic; |
dat: std_logic_vector(31 downto 0); |
end record; |
|
type soc_wbm_in_type is record |
ack: std_logic; |
dat: std_logic_vector(31 downto 0); |
end record; |
|
type soc_wbm_out_type is record |
cyc: std_logic; |
stb: std_logic; |
we: std_logic; |
sel: std_logic_vector(3 downto 0); |
adr: std_logic_vector(27 downto 2); |
dat: std_logic_vector(31 downto 0); |
end record; |
|
type monitor_out_type is record |
data: std_logic_vector(31 downto 0); |
valid: std_logic; |
end record; |
|
procedure load_ram( |
filename: string; |
signal clk: in std_logic; |
signal soc_in: out soc_wbs_in_type; |
signal soc_out: in soc_wbs_out_type |
); |
|
procedure run_test( |
filename: string; |
signal clk: in std_logic; |
signal globals: out soc_globals_type; |
signal soc_in: out soc_wbs_in_type; |
signal soc_out: in soc_wbs_out_type; |
signal result: in monitor_out_type |
); |
end package; |
/lxp32/tags/1.0/verify/lxp32/src/tb/monitor.vhd
0,0 → 1,80
--------------------------------------------------------------------- |
-- Test monitor |
-- |
-- Part of the LXP32 testbench |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Provide means for a test platform to interact with the testbench. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
use work.common_pkg.all; |
use work.tb_pkg.all; |
|
entity monitor is |
generic( |
VERBOSE: boolean |
); |
port( |
clk_i: in std_logic; |
rst_i: in std_logic; |
|
wbs_cyc_i: in std_logic; |
wbs_stb_i: in std_logic; |
wbs_we_i: in std_logic; |
wbs_sel_i: in std_logic_vector(3 downto 0); |
wbs_ack_o: out std_logic; |
wbs_adr_i: in std_logic_vector(27 downto 2); |
wbs_dat_i: in std_logic_vector(31 downto 0); |
wbs_dat_o: out std_logic_vector(31 downto 0); |
|
finished_o: out std_logic; |
result_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture sim of monitor is |
|
signal result: std_logic_vector(31 downto 0):=(others=>'0'); |
signal finished: std_logic:='0'; |
|
begin |
|
wbs_ack_o<=wbs_cyc_i and wbs_stb_i; |
wbs_dat_o<=(others=>'0'); |
|
finished_o<=finished; |
result_o<=result; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if rst_i='1' then |
finished<='0'; |
result<=(others=>'0'); |
elsif wbs_cyc_i='1' and wbs_stb_i='1' and wbs_we_i='1' then |
assert wbs_sel_i="1111" |
report "Monitor doesn't support byte-granular access "& |
"(SEL_I() is 0x"&hex_string(wbs_sel_i)&")" |
severity failure; |
|
if VERBOSE then |
report "Monitor: value "& |
"0x"&hex_string(wbs_dat_i)& |
" written to address "& |
"0x"&hex_string(wbs_adr_i); |
end if; |
|
if unsigned(wbs_adr_i)=to_unsigned(0,wbs_adr_i'length) then |
result<=wbs_dat_i; |
finished<='1'; |
end if; |
end if; |
end if; |
end process; |
|
end architecture; |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test001.asm
0,0 → 1,230
/* |
* This test verifies that basic instructions |
* (data transfers, addition/subtraction, jumps) work. |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, bad_jump |
|
// All registers should be zero-initialized after reset |
lc r0, jump0 |
add r1, r1, 1 |
cjmpe r0, r1, 1 |
|
sw r100, 2 // failure: r1 not initialized |
jmp r101 |
|
// Test different jump conditions |
jump0: |
lc r0, jump1 |
jmp r0 |
sw r100, 3 // failure: this instruction should not be reachable |
jmp r101 |
|
jump1: |
lc r0, jump2 |
mov r1, 100 |
cjmpne r0, r1, 101 |
sw r100, 4 // failure: required jump is not taken |
jmp r101 |
|
jump2: |
lc r0, jump3 |
cjmpe r0, r1, 100 |
sw r100, 5 // failure: required jump is not taken |
jmp r101 |
|
jump3: |
lc r0, jump4 |
cjmpuge r0, r1, 99 |
sw r100, 6 // failure: required jump is not taken |
jmp r101 |
|
jump4: |
lc r0, jump5 |
cjmpuge r0, r1, 100 |
sw r100, 7 // failure: required jump is not taken |
jmp r101 |
|
jump5: |
lc r0, jump6 |
cjmpug r0, r1, 99 |
sw r100, 8 // failure: required jump is not taken |
jmp r101 |
|
jump6: |
lc r0, jump7 |
cjmpsge r0, r1, -128 |
sw r100, 9 // failure: required jump is not taken |
jmp r101 |
|
jump7: |
lc r0, jump8 |
cjmpsge r0, r1, 100 |
sw r100, 10 // failure: required jump is not taken |
jmp r101 |
|
jump8: |
lc r0, jump9 |
cjmpsg r0, r1, 99 |
sw r100, 11 // failure: required jump is not taken |
jmp r101 |
|
jump9: |
lc r0, 2227053353 |
lc r1, 2933288161 |
cjmpug r102, r0, r1 |
|
lc r0, 3957963761 |
lc r1, 4048130130 |
cjmpug r102, r0, r1 |
|
lc r0, 1021028019 |
lc r1, 2570980487 |
cjmpug r102, r0, r1 |
|
lc r0, 470638116 |
lc r1, 3729241862 |
cjmpug r102, r0, r1 |
|
lc r0, 2794175299 |
lc r1, 3360494259 |
cjmpug r102, r0, r1 |
|
lc r0, 522532873 |
lc r1, 2103051039 |
cjmpug r102, r0, r1 |
|
lc r0, 994440598 |
lc r1, 4241216605 |
cjmpug r102, r0, r1 |
|
lc r0, 176753939 |
lc r1, 850320156 |
cjmpug r102, r0, r1 |
|
lc r0, 3998259744 |
lc r1, 4248205376 |
cjmpug r102, r0, r1 |
|
lc r0, 3695803806 |
lc r1, 4130490642 |
cjmpug r102, r0, r1 |
|
lc r0, -798605244 |
lc r1, -233549907 |
cjmpsg r102, r0, r1 |
|
lc r0, -1221540757 |
lc r1, 580991794 |
cjmpsg r102, r0, r1 |
|
lc r0, -1651432714 |
lc r1, -635466783 |
cjmpsg r102, r0, r1 |
|
lc r0, 43633328 |
lc r1, 1235055289 |
cjmpsg r102, r0, r1 |
|
lc r0, -2132159079 |
lc r1, -981565396 |
cjmpsg r102, r0, r1 |
|
lc r0, -859182414 |
lc r1, -697843885 |
cjmpsg r102, r0, r1 |
|
lc r0, 1720638509 |
lc r1, 2127959231 |
cjmpsg r102, r0, r1 |
|
lc r0, -1888878751 |
lc r1, 1230499715 |
cjmpsg r102, r0, r1 |
|
lc r0, 517066081 |
lc r1, 1914084509 |
cjmpsg r102, r0, r1 |
|
lc r0, -266475918 |
lc r1, 2001358724 |
cjmpsg r102, r0, r1 |
|
mov r1, 100 |
cjmpe r102, r1, 101 |
cjmpne r102, r1, 100 |
cjmpuge r102, r1, 101 |
cjmpug r102, r1, 100 |
cjmpug r102, r1, 101 |
cjmpsge r102, r1, 101 |
cjmpsg r102, r1, 101 |
cjmpsg r102, r1, 100 |
cjmpsg r102, -128, r1 |
lc r0, jump10 |
jmp r0 |
|
bad_jump: |
sw r100, 12 // failure: jump should not be taken |
jmp r101 |
|
jump10: |
|
// Copy itself to another portion of memory |
mov r0, 0 // source pointer |
lc r1, 0x00008000 // destination pointer |
lc r2, end // size of block to copy, in bytes |
lc r32, copy_loop |
|
copy_loop: |
lw r3, r0 |
sw r1, r3 |
add r0, r0, 4 |
add r1, r1, 4 |
cjmpul r32, r0, r2 |
|
// Calculate sum of program body in a post-condition loop |
mov r0, 0 // pointer |
mov r16, 0 // sum |
lc r32, sum_loop |
|
sum_loop: |
lw r1, r0 |
add r16, r16, r1 |
add r0, r0, 4 |
cjmpul r32, r0, r2 |
|
// Calculate sum of copied program body with negative sign, in a pre-condition loop |
lc r0, 0x00008000 // pointer |
add r2, r0, r2 // end pointer |
mov r17, 0 // sum |
lc r32, sum2_loop |
lc r33, sum2_end |
|
sum2_loop: |
cjmpuge r33, r0, r2 |
lw r1, r0 |
sub r17, r17, r1 |
add r0, r0, 4 |
jmp r32 |
sw r100, 13 // failure: this instruction should not be reachable |
jmp r101 |
|
sum2_end: |
|
// Check that sums are equal (but with opposite signs) |
add r0, r16, r17 // r0 should be zero now |
lc r32, success |
cjmpe r32, r0, 0 |
sw r100, 14 // failure: results do not match |
jmp r101 |
|
success: |
sw r100, 1 |
|
halt: |
hlt |
jmp r101 |
|
end: |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test010.asm
0,0 → 1,54
/* |
* This test verifies interrupt handling using a simple timer model |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
lc r103, 0x20000000 // timer: number of pulses (0xFFFFFFFF - infinite) |
lc r104, 0x20000004 // timer: delay between pulses (in cycles) |
|
lc iv0, timer_handler0 |
lc iv1, timer_handler1 |
mov cr, 3 // enable interrupts 0 and 1 |
|
lc r32, 2000 // cycle counter |
lc r33, cnt_loop |
mov r34, 0 // interrupt 0 call counter |
mov r35, 0 // interrupt 1 call counter |
|
sw r104, 100 |
sw r103, 10 |
|
cnt_loop: |
sub r32, r32, 1 |
cjmpug r33, r32, 0 // cnt_loop |
|
cjmpne r102, r34, 10 // failure |
cjmpne r102, r35, 4 // failure |
|
sw r100, 1 |
jmp r101 // halt |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 // halt |
|
timer_handler0: |
add r34, r34, 1 |
lc r0, 0x10000004 |
sw r0, r34 |
cjmpne irp, r34, 5 // exit interrupt handler if r34!=5 |
mov cr, 1 // disable interrupt 1 |
iret |
|
timer_handler1: |
add r35, r35, 1 |
// Interrupt 1 has lower priority than interrupt 0 and will be called later |
cjmpne r102, r34, r35 |
lc r0, 0x10000008 |
sw r0, r35 |
iret |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test002.asm
0,0 → 1,93
/* |
* This test calculates a few Fibonacci sequence members |
* end compares them to pre-calculated values. |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
|
// Calculate Fibonacci sequence members |
mov r16, 0 // current member |
mov r17, 1 // next member |
lc r18, 0 // counter |
lc r19, 0x00008000 // destination pointer |
lc r32, calc_loop |
|
calc_loop: |
sw r19, r16 |
add r19, r19, 4 |
add r18, r18, 1 |
add r0, r16, r17 |
mov r16, r17 |
mov r17, r0 |
cjmpul r32, r18, 40 |
|
// Compare |
lc r16, 0x00008000 |
lc r17, expected |
mov r18, 0 // counter |
lc r32, comp_loop |
lc r33, comp_differ |
|
comp_loop: |
lw r0, r16 |
lw r1, r17 |
cjmpne r33, r0, r1 |
add r16, r16, 4 |
add r17, r17, 4 |
add r18, r18, 1 |
cjmpul r32, r18, 40 |
|
// Everything seems to be OK |
sw r100, 1 |
|
halt: |
hlt |
jmp r101 |
|
comp_differ: |
sw r100, 2 |
jmp r101 |
|
// Expected (pre-calculated) values |
expected: |
.word 0 |
.word 1 |
.word 1 |
.word 2 |
.word 3 |
.word 5 |
.word 8 |
.word 13 |
.word 21 |
.word 34 |
.word 55 |
.word 89 |
.word 144 |
.word 233 |
.word 377 |
.word 610 |
.word 987 |
.word 1597 |
.word 2584 |
.word 4181 |
.word 6765 |
.word 10946 |
.word 17711 |
.word 28657 |
.word 46368 |
.word 75025 |
.word 121393 |
.word 196418 |
.word 317811 |
.word 514229 |
.word 832040 |
.word 1346269 |
.word 2178309 |
.word 3524578 |
.word 5702887 |
.word 9227465 |
.word 14930352 |
.word 24157817 |
.word 39088169 |
.word 63245986 |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test011.asm
0,0 → 1,172
/* |
* Coroutine switching test |
* |
* There are two coroutines in this test. The main coroutine calculates |
* CRC32 of the 1024-byte data block. The secondary coroutine simulates |
* a linear-feedback shift register (LFSR) with a polynom 1+x^6+x^7. |
* It is invoked by the timer interrupt. |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
lc r103, 0x20000000 // timer: number of pulses (0xFFFFFFFF - infinite) |
lc r104, 0x20000004 // timer: delay between pulses (in cycles) |
|
// Set up secondary routine (invoked by timer) |
mov r200, 127 // initial value |
lc r201, 250 // counter (number of LFSR iterations) |
mov r204, 0 // result |
lc r210, 0x10000008 // secondary routine output pointer |
|
lc iv0, timer_handler |
mov cr, 1 // enable interrupt 0 |
|
sw r104, 100 |
lc r0, 0xFFFFFFFF |
sw r103, r0 // activate timer, unlimited pulses |
|
// Main routine |
lc r16, 0x10000004 // output pointer |
lc r17, 0xFFFFFFFF // initial CRC value |
lc r18, 0xEDB88320 // polynom |
lc r19, data // input pointer |
lc r20, 256 // data block size in words |
|
lc r32, word_loop |
lc r33, bit_loop |
lc r34, dont_xor |
|
mov r64, 0 // word counter |
|
word_loop: |
lw r0, r19 |
mov r65, 0 // bit counter |
|
bit_loop: |
and r1, r0, 1 |
and r2, r17, 1 |
sru r17, r17, 1 |
xor r3, r1, r2 |
cjmpe r34, r3, 0 // dont_xor |
xor r17, r17, r18 |
|
dont_xor: |
sru r0, r0, 1 |
add r65, r65, 1 |
cjmpul r33, r65, 32 // bit_loop |
|
sw r16, r17 |
add r19, r19, 4 |
add r64, r64, 1 |
cjmpul r32, r64, r20 // word_loop |
|
not r17, r17 |
sw r16, r17 |
|
// Check main routine result |
lc r0, 0x501860E6 |
cjmpne r102, r0, r17 |
|
// Wait until secondary routine finishes its job |
lc r0, wait_secondary |
|
wait_secondary: |
cjmpe r0, r204, 0 |
|
// Check secondary routine result |
cjmpne r102, r204, 0x57 |
|
sw r103, 0 // deactivate timer |
|
sw r100, 1 |
jmp r101 |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 |
|
timer_handler: |
sru r202, r200, 5 |
sru r203, r200, 6 |
xor r202, r202, r203 |
and r202, r202, 1 |
sl r200, r200, 1 |
or r200, r200, r202 |
and r200, r200, 0x7f |
sub r201, r201, 1 |
cjmpug irp, r201, 0 // exit interrupt handler if r201>0 |
mov r204, r200 |
sw r210, r204 |
mov cr, 0 // disable further interrupts |
iret |
|
.align |
data: |
.byte 0x00, 0x90, 0x31, 0xCF, 0x95, 0x7A, 0x59, 0xE5, 0xD2, 0xBF, 0x2C, 0xDB, 0xB5, 0x83, 0x4D, 0x03 |
.byte 0x17, 0x5D, 0x25, 0x2A, 0xFD, 0x72, 0x1E, 0x01, 0x02, 0x60, 0x88, 0x92, 0x9A, 0x9B, 0x2A, 0xA9 |
.byte 0x73, 0x5A, 0x0E, 0x9B, 0xC8, 0xCD, 0x85, 0x4D, 0xE0, 0xBA, 0xF4, 0xEC, 0x8A, 0x24, 0x76, 0x3C |
.byte 0xDC, 0x35, 0xC7, 0xD7, 0xFF, 0xFF, 0x9C, 0x64, 0x44, 0x4C, 0xD7, 0x06, 0x60, 0x17, 0xAD, 0x0E |
.byte 0x02, 0xEB, 0x46, 0x45, 0x96, 0xB0, 0xD6, 0xB9, 0x7C, 0x34, 0xBE, 0x77, 0x75, 0xF2, 0xBE, 0x1B |
.byte 0x99, 0x62, 0xBC, 0x9B, 0x92, 0x5C, 0x26, 0x39, 0x6C, 0xCD, 0x84, 0xFD, 0xC0, 0x58, 0x2B, 0xA8 |
.byte 0x7D, 0x10, 0xB3, 0x81, 0x25, 0xF3, 0x24, 0xE7, 0xB1, 0x4D, 0x6D, 0x12, 0xF7, 0xAE, 0x27, 0xE0 |
.byte 0xD2, 0x95, 0x30, 0x2D, 0xD1, 0x79, 0x27, 0x81, 0xBB, 0x67, 0x47, 0x91, 0xAE, 0xC1, 0xB8, 0x79 |
.byte 0x1F, 0x5E, 0xD5, 0x08, 0x84, 0xA9, 0x6D, 0x1A, 0xF3, 0xEB, 0x8C, 0x58, 0x78, 0x5F, 0xD8, 0x51 |
.byte 0x74, 0x45, 0xFB, 0x4C, 0xBD, 0x91, 0x32, 0xC2, 0xD6, 0x65, 0x80, 0xE3, 0x07, 0xFE, 0x92, 0x0C |
.byte 0x88, 0x31, 0xD7, 0xA0, 0xA8, 0x32, 0xD7, 0x1F, 0x1C, 0xBE, 0x50, 0xF0, 0x49, 0x56, 0x23, 0xBB |
.byte 0xD5, 0xB5, 0x99, 0xBF, 0x40, 0x24, 0x00, 0x0F, 0xCE, 0xDA, 0x35, 0x1D, 0x8D, 0x03, 0x1D, 0x74 |
.byte 0xC0, 0xAF, 0x8B, 0x12, 0x6F, 0x33, 0xB2, 0x4A, 0x6F, 0x3B, 0x93, 0x88, 0xA0, 0x29, 0x81, 0xF6 |
.byte 0xB2, 0xEC, 0x30, 0x56, 0x2D, 0xFE, 0x75, 0xFF, 0x18, 0xA0, 0x18, 0x70, 0xEE, 0x0C, 0xE5, 0x4A |
.byte 0x3A, 0xC4, 0x69, 0x33, 0xA0, 0x9A, 0x73, 0x77, 0x99, 0xA2, 0xDA, 0xD4, 0x9F, 0xB8, 0x90, 0x60 |
.byte 0x2F, 0xBC, 0x8E, 0xE7, 0x3E, 0x30, 0x9A, 0xB2, 0x95, 0x59, 0x7E, 0x14, 0xBD, 0x9C, 0x9E, 0xB0 |
.byte 0xCD, 0x26, 0x93, 0xDE, 0xE9, 0x9D, 0xBA, 0x0B, 0xAA, 0xF9, 0x50, 0x91, 0x4E, 0x2C, 0x1B, 0xDE |
.byte 0xD9, 0xBE, 0x27, 0x53, 0x14, 0x13, 0xA4, 0xD1, 0x8B, 0x72, 0x68, 0x4C, 0x77, 0x80, 0x27, 0x52 |
.byte 0xBC, 0x50, 0xD3, 0xF5, 0xDF, 0xB9, 0x4C, 0xF1, 0x20, 0x10, 0xC8, 0x86, 0x9C, 0xF4, 0x12, 0xE0 |
.byte 0xA7, 0x52, 0x1A, 0x81, 0x3A, 0x4A, 0xEB, 0x8D, 0xA9, 0x1D, 0x7E, 0x61, 0x7F, 0xCB, 0x82, 0x61 |
.byte 0xB0, 0x88, 0x9B, 0x65, 0x01, 0xB5, 0x19, 0x9F, 0xDC, 0x7D, 0xBF, 0x7F, 0x61, 0xC9, 0x8D, 0x5B |
.byte 0xF4, 0xA1, 0x2D, 0x5F, 0x21, 0xBE, 0xF3, 0x9C, 0x07, 0x54, 0x0E, 0xA3, 0x21, 0xD8, 0xDD, 0x98 |
.byte 0xB8, 0xDA, 0x03, 0x20, 0xB5, 0x9D, 0x37, 0x10, 0x2B, 0x9F, 0x57, 0x51, 0x5E, 0xA9, 0xCD, 0xCE |
.byte 0x86, 0x9C, 0xCC, 0xE6, 0x25, 0xA1, 0x67, 0x40, 0x22, 0xDA, 0x10, 0x6D, 0x92, 0x4D, 0x8C, 0x39 |
.byte 0x4F, 0x1C, 0xCE, 0x22, 0x48, 0xC9, 0xE5, 0xCA, 0xBE, 0x9D, 0x5C, 0xDB, 0x3A, 0xDD, 0x3A, 0x3F |
.byte 0x8A, 0xFC, 0x0D, 0x14, 0x86, 0x6D, 0x18, 0x42, 0xE4, 0x3B, 0x25, 0x20, 0xEE, 0x14, 0x0C, 0x0F |
.byte 0x56, 0xEA, 0x66, 0x6E, 0xF2, 0xD6, 0x88, 0xD7, 0xB1, 0x65, 0x42, 0x01, 0x86, 0xF4, 0x66, 0x3D |
.byte 0x95, 0x41, 0xAF, 0xF1, 0x6F, 0xE3, 0x01, 0xF0, 0x99, 0xC9, 0x93, 0x24, 0x38, 0x62, 0x01, 0x6A |
.byte 0x15, 0xA8, 0xDA, 0x10, 0xCF, 0xA9, 0xB1, 0xCD, 0x87, 0xAF, 0x24, 0xB0, 0xBA, 0xC7, 0x07, 0xDE |
.byte 0xA4, 0xB4, 0x15, 0x8D, 0xF2, 0x0F, 0x46, 0x25, 0xFB, 0x9E, 0x4A, 0xEC, 0x5E, 0xB1, 0x37, 0x27 |
.byte 0x3D, 0x85, 0xE6, 0x1B, 0xE7, 0x71, 0x14, 0xC8, 0x2B, 0xF9, 0xC6, 0xDE, 0x35, 0x74, 0x00, 0xC0 |
.byte 0x1D, 0x67, 0x4F, 0xFE, 0x09, 0x40, 0x30, 0x3F, 0x27, 0x9E, 0xE2, 0xF0, 0x32, 0xC7, 0xA5, 0xA8 |
.byte 0xE8, 0x74, 0xEB, 0xAA, 0x26, 0xA2, 0x91, 0x6C, 0xF1, 0x8A, 0x94, 0x89, 0x41, 0x65, 0x59, 0x09 |
.byte 0xCB, 0x32, 0x11, 0x63, 0x97, 0x12, 0x32, 0x27, 0xA4, 0x74, 0x9A, 0xB2, 0x70, 0xAF, 0x65, 0xD5 |
.byte 0x97, 0x33, 0xF3, 0xE0, 0x64, 0xFC, 0x2F, 0xE5, 0x93, 0x71, 0xA0, 0xB5, 0x0C, 0x49, 0x42, 0x68 |
.byte 0xE5, 0xB5, 0xBA, 0xE5, 0x64, 0x65, 0xE7, 0x4F, 0x63, 0x92, 0x5A, 0xBB, 0xBE, 0xBC, 0xBD, 0x23 |
.byte 0x33, 0x45, 0xAE, 0xE9, 0x5E, 0x84, 0x1C, 0xE8, 0x33, 0x84, 0xA7, 0x70, 0xB0, 0x17, 0x14, 0x13 |
.byte 0x07, 0x5A, 0x4E, 0xB2, 0x24, 0x65, 0x11, 0xAC, 0xB9, 0x32, 0xB1, 0x9D, 0xA7, 0x8B, 0x19, 0x8B |
.byte 0x0C, 0xF9, 0x76, 0xF8, 0xBA, 0x87, 0xAE, 0xB1, 0x5F, 0x63, 0x0C, 0xCF, 0x2B, 0x0F, 0x4F, 0xC8 |
.byte 0x36, 0x52, 0x7C, 0x02, 0x70, 0x82, 0x9B, 0xC2, 0x66, 0x59, 0xD7, 0xF1, 0x9F, 0xFF, 0x0D, 0x90 |
.byte 0xDD, 0x63, 0x4E, 0x48, 0x06, 0x9C, 0x64, 0x04, 0x07, 0x74, 0xDB, 0xEF, 0x66, 0xBA, 0x9C, 0xCE |
.byte 0xE0, 0x95, 0x98, 0x14, 0xCB, 0x76, 0x97, 0x96, 0x91, 0xD0, 0xAC, 0x57, 0x02, 0x44, 0x57, 0x3B |
.byte 0xC5, 0x5F, 0xDE, 0x1D, 0xB9, 0xA1, 0xE4, 0x2E, 0x89, 0xE6, 0xC7, 0xF7, 0x32, 0xE6, 0xCC, 0xF4 |
.byte 0xD9, 0xE3, 0xA0, 0x2F, 0x9D, 0x43, 0x40, 0xBB, 0xC9, 0x2B, 0xB6, 0x7B, 0x14, 0xCE, 0xDA, 0x1F |
.byte 0x4E, 0x92, 0x79, 0xC3, 0x31, 0xBA, 0xFE, 0x03, 0xA5, 0xB1, 0x2C, 0x12, 0x46, 0xAD, 0xD6, 0x8E |
.byte 0x5F, 0xC7, 0x3B, 0xA3, 0x3B, 0x34, 0xF8, 0x48, 0x05, 0xC4, 0x26, 0x0C, 0x02, 0x59, 0xA4, 0x57 |
.byte 0x6A, 0x6A, 0x17, 0x8B, 0xB3, 0x56, 0xA8, 0xE0, 0x89, 0x91, 0x0D, 0x77, 0x42, 0x6D, 0xDD, 0x7C |
.byte 0x16, 0x90, 0xB5, 0xC7, 0xDD, 0xD9, 0x4B, 0xDD, 0xA5, 0xBF, 0xD4, 0xC3, 0xDF, 0xE8, 0xE9, 0x85 |
.byte 0x70, 0x1B, 0x5A, 0xD2, 0x6D, 0x2B, 0x00, 0xA8, 0xC6, 0x10, 0x16, 0x62, 0xAF, 0xCE, 0x27, 0x24 |
.byte 0x0C, 0x58, 0x04, 0xFA, 0xA5, 0x0D, 0xEA, 0xA3, 0x6E, 0x05, 0x3E, 0x64, 0xA7, 0xC8, 0x05, 0xD3 |
.byte 0x23, 0xA2, 0x8C, 0xFB, 0x76, 0x36, 0x4C, 0xC8, 0x57, 0x7B, 0x9B, 0x1A, 0xFC, 0xC1, 0x26, 0x74 |
.byte 0xB6, 0xFF, 0xC6, 0xA4, 0x9F, 0xF2, 0xB0, 0x4A, 0x91, 0x4A, 0x8C, 0xB6, 0x3F, 0x8B, 0x7E, 0xF3 |
.byte 0xAB, 0xC3, 0x9F, 0x72, 0xCC, 0xBE, 0xFD, 0x34, 0xA1, 0xE8, 0x97, 0xEB, 0x81, 0x7D, 0x73, 0xE3 |
.byte 0xF0, 0x2E, 0x40, 0x33, 0xBB, 0xF0, 0xA0, 0x0A, 0xA4, 0x08, 0x8E, 0x8B, 0x72, 0x0F, 0xFE, 0x20 |
.byte 0x99, 0x0C, 0x2C, 0xA9, 0x55, 0x50, 0xA7, 0x69, 0x6D, 0x38, 0xAD, 0x2A, 0x7F, 0x81, 0xCD, 0x6F |
.byte 0x01, 0x56, 0x60, 0x20, 0xD3, 0xBB, 0xE4, 0xA4, 0xA6, 0x83, 0xBB, 0xBC, 0xF5, 0x77, 0x5C, 0x1C |
.byte 0xE7, 0xD2, 0x74, 0x1B, 0xDE, 0xC2, 0x0A, 0x6A, 0xEF, 0x11, 0x2A, 0x36, 0x1D, 0x97, 0x1E, 0x9E |
.byte 0x94, 0xB0, 0xBB, 0xEA, 0xAB, 0x4C, 0xCF, 0x60, 0xFF, 0xC5, 0x34, 0x2B, 0x63, 0x2E, 0x94, 0x32 |
.byte 0xF5, 0x30, 0x61, 0x4D, 0x1E, 0x32, 0x0B, 0xC5, 0xC2, 0xE1, 0x00, 0x72, 0x6D, 0xCD, 0x76, 0x7E |
.byte 0xBF, 0x3D, 0x8F, 0x17, 0xEC, 0xE4, 0xDB, 0x0F, 0x7D, 0xA1, 0xBF, 0xC1, 0x41, 0xE8, 0xCA, 0x33 |
.byte 0x8D, 0x0D, 0x85, 0xCA, 0xB4, 0x06, 0xBB, 0x8D, 0xEA, 0xE0, 0xCC, 0x4E, 0x65, 0x7B, 0x0D, 0xA6 |
.byte 0x00, 0xC4, 0xC1, 0x38, 0x27, 0x0E, 0xAB, 0x08, 0x58, 0xB2, 0xCC, 0x6F, 0xFB, 0xA2, 0x4B, 0x7A |
.byte 0xDF, 0x14, 0x19, 0x24, 0x24, 0xEB, 0x4F, 0x5F, 0xD0, 0x0B, 0xD0, 0x3D, 0xE5, 0x41, 0x44, 0x35 |
.byte 0x3A, 0xD8, 0xDF, 0xE3, 0xD8, 0x9B, 0x0A, 0x2C, 0x2F, 0x5B, 0x73, 0x2F, 0xE4, 0x9F, 0x8B, 0xE9 |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test003.asm
0,0 → 1,43
/* |
* This test verifies that basic logical operations |
* (and, xor, or, not) work. |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
|
lc r0, 0xD54B65C0 |
lc r1, 0xCE8870A8 |
lc r16, 0x10000004 // destination pointer |
|
and r2, r0, r1 |
sw r16, r2 |
lc r3, 0xC4086080 |
cjmpne r102, r2, r3 |
|
or r2, r0, r1 |
sw r16, r2 |
lc r3, 0xDFCB75E8 |
cjmpne r102, r2, r3 |
|
xor r2, r0, r1 |
sw r16, r2 |
lc r3, 0x1BC31568 |
cjmpne r102, r2, r3 |
|
// Note: "not dst, src" is just an alias for "xor dst, src, -1" |
not r2, r0 |
sw r16, r2 |
lc r3, 0x2AB49A3F |
cjmpne r102, r2, r3 |
|
sw r100, 1 |
jmp r101 |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test012.asm
0,0 → 1,373
/* |
* Test multiplication |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
lc r103, 0x10000004 |
|
// Check multiplication table |
|
lc r32, op1 |
lc r33, op2 |
lc r34, product |
|
lc r35, loop |
mov r10, 100 |
|
loop: |
lw r0, r32 |
lw r1, r33 |
mul r3, r0, r1 |
lw r2, r34 |
cjmpne r102, r2, r3 // failure |
add r32, r32, 4 |
add r33, r33, 4 |
add r34, r34, 4 |
sub r10, r10, 1 |
cjmpug r35, r10, 0 // loop |
|
// Simulate a linear congruent random number generator |
// as described in ISO/IEC 9899:1999 ("C99" standard) |
|
mov r64, 1 // initial value |
lc r65, 1103515245 // multiplier |
lc r66, 12345 // addition constant |
lc r67, 32767 // mask |
|
mov r10, 100 |
lc r32, rand |
lc r35, rnd_loop |
|
rnd_loop: |
call r32 // rand |
sw r103, r0 |
sub r10, r10, 1 |
cjmpug r35, r10, 0 // rnd_loop |
|
lc r1, 26521 |
cjmpne r102, r0, r1 // failure |
|
sw r100, 1 |
jmp r101 // halt |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 // halt |
|
rand: |
mul r64, r64, r65 |
add r64, r64, r66 |
sru r0, r64, 16 |
and r0, r0, r67 |
ret |
|
op1: |
.word 5 |
.word 1021028019 |
.word 3360494259 |
.word 4241216605 |
.word 3998259744 |
.word 4061417389 |
.word 2643534582 |
.word 3313401900 |
.word 2127959231 |
.word 517066081 |
.word 971560706 |
.word 2765397972 |
.word 1972360008 |
.word 5267214 |
.word 3667030040 |
.word 3641602966 |
.word 297473786 |
.word 3637724660 |
.word 3268423030 |
.word 2775202737 |
.word 2509905173 |
.word 3885678420 |
.word 820072030 |
.word 1177172069 |
.word 1047341530 |
.word 1826819693 |
.word 1694831631 |
.word 4270696691 |
.word 1299413127 |
.word 2817552511 |
.word 2675818186 |
.word 231984537 |
.word 65817694 |
.word 3640600522 |
.word 545754317 |
.word 2973624217 |
.word 2669208735 |
.word 1079210895 |
.word 660979290 |
.word 738800690 |
.word 1631093128 |
.word 489263629 |
.word 4116157872 |
.word 3178886821 |
.word 2324324955 |
.word 2982397572 |
.word 3365621776 |
.word 2764926001 |
.word 1498466135 |
.word 1012944436 |
.word -246837166 |
.word 470638116 |
.word 2103051039 |
.word 176753939 |
.word -164476654 |
.word -1221540757 |
.word 43633328 |
.word -859182414 |
.word 1230499715 |
.word -266475918 |
.word 1949507090 |
.word -856415694 |
.word 737657061 |
.word 118818171 |
.word 359218821 |
.word 1246427092 |
.word -615317895 |
.word 1029267653 |
.word 628714057 |
.word -2029278762 |
.word -1700206210 |
.word -785893232 |
.word -492445025 |
.word -1032292742 |
.word -1506164940 |
.word 63097629 |
.word -1909659707 |
.word 1003528447 |
.word 1095156443 |
.word 325606314 |
.word 1750055165 |
.word 861103782 |
.word -1441910348 |
.word 1113139065 |
.word -1337158265 |
.word -381613907 |
.word 567364862 |
.word -808095798 |
.word -1503281503 |
.word -1646541226 |
.word -405777159 |
.word -98798373 |
.word -1948683123 |
.word -1889476128 |
.word 994740197 |
.word 57636334 |
.word -1628834166 |
.word -15689588 |
.word 1568614403 |
.word 722317890 |
|
op2: |
.word 3 |
.word 2570980487 |
.word 2794175299 |
.word 994440598 |
.word 4248205376 |
.word 3496362052 |
.word 3659500513 |
.word 2162808217 |
.word 1720638509 |
.word 1914084509 |
.word 3385545516 |
.word 3476389465 |
.word 706599398 |
.word 3196936660 |
.word 2983089252 |
.word 1751592726 |
.word 2284105389 |
.word 4111191592 |
.word 3563638217 |
.word 2361468945 |
.word 3236010945 |
.word 1439769561 |
.word 1910111900 |
.word 2038606217 |
.word 311877730 |
.word 538878248 |
.word 3378587675 |
.word 1911288510 |
.word 3196239568 |
.word 2503116372 |
.word 457280137 |
.word 3023717616 |
.word 98988876 |
.word 4098692253 |
.word 2797102115 |
.word 2159140448 |
.word 637551125 |
.word 2125433411 |
.word 2902980138 |
.word 395247949 |
.word 1645373547 |
.word 1922640262 |
.word 1835827423 |
.word 3088444407 |
.word 4150011791 |
.word 273297575 |
.word 673792897 |
.word 2743197342 |
.word 656195007 |
.word 2550324965 |
.word -337003535 |
.word -565725434 |
.word 522532873 |
.word 850320156 |
.word -599163490 |
.word 580991794 |
.word 1235055289 |
.word -697843885 |
.word -1888878751 |
.word 2001358724 |
.word -705426274 |
.word 764466127 |
.word 1149731192 |
.word 528626491 |
.word 74073848 |
.word 1986549712 |
.word 1021657893 |
.word 1502226327 |
.word 339763464 |
.word -248542057 |
.word -1245542745 |
.word 1721855193 |
.word 1203018508 |
.word -551112349 |
.word -245462721 |
.word -2079899329 |
.word -603441594 |
.word 2123105909 |
.word 821726251 |
.word -167975413 |
.word 343634678 |
.word 279038731 |
.word -1328164947 |
.word -121529220 |
.word 760122714 |
.word -710590105 |
.word -1288727804 |
.word -829054509 |
.word 1774242228 |
.word 198747848 |
.word 2017990513 |
.word -1246218417 |
.word 135357763 |
.word -193250194 |
.word 1080138546 |
.word -278225361 |
.word 1216454983 |
.word -808631398 |
.word 1236885385 |
.word -1142156106 |
|
product: |
.word 15 |
.word 3187625061 |
.word 4060456409 |
.word 3720114046 |
.word 2133166080 |
.word 2956880372 |
.word 39144502 |
.word 4048238156 |
.word 3781675411 |
.word 2552762493 |
.word 2186320472 |
.word 2004478132 |
.word 1215814320 |
.word 1967719832 |
.word 961259872 |
.word 3218110692 |
.word 808743666 |
.word 1758021152 |
.word 3653971878 |
.word 1641193153 |
.word 1749066709 |
.word 2447944244 |
.word 1085998408 |
.word 3424443149 |
.word 1143260532 |
.word 542297608 |
.word 3545920917 |
.word 820648538 |
.word 131342256 |
.word 3650446252 |
.word 3798859290 |
.word 1641505648 |
.word 3270043112 |
.word 2203660002 |
.word 2262848519 |
.word 3335948128 |
.word 301711115 |
.word 853759085 |
.word 1108457156 |
.word 3991332106 |
.word 3268240344 |
.word 4293625806 |
.word 2556650576 |
.word 2577362995 |
.word 2650590677 |
.word 1310146588 |
.word 247957520 |
.word 2454471742 |
.word 1425274089 |
.word 39783556 |
.word -2091990222 |
.word -1969753896 |
.word 1297343511 |
.word -32067820 |
.word -824646884 |
.word -1038744090 |
.word -615130832 |
.word 1160766902 |
.word -878690909 |
.word -1535377208 |
.word -1071814884 |
.word -139010962 |
.word 18403416 |
.word 2137375833 |
.word 2082402008 |
.word 1027762240 |
.word 1775596157 |
.word 1957173043 |
.word 1934687048 |
.word -1391708870 |
.word 1441023282 |
.word 897888784 |
.word -2134317964 |
.word 1520184110 |
.word -123674164 |
.word -464692957 |
.word -481104418 |
.word -946631029 |
.word 55606985 |
.word -2060538802 |
.word 1653304606 |
.word 1163987746 |
.word -2138299740 |
.word 1903549084 |
.word 1936321654 |
.word -455120229 |
.word 1931283960 |
.word 617295230 |
.word -470388172 |
.word -522275024 |
.word -1465917207 |
.word 541793429 |
.word -1975377433 |
.word 1462865472 |
.word 1922773434 |
.word 1925941938 |
.word 1115302470 |
.word 604268600 |
.word -32471397 |
.word 948036332 |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test004.asm
0,0 → 1,548
/* |
* This test verifies that nop instruction does not change |
* register values. |
*/ |
|
lc r0, failure |
nop |
mov r1, 1 |
nop |
mov r2, 2 |
mov r3, 3 |
nop |
mov r4, 4 |
mov r5, 5 |
mov r6, 6 |
nop |
nop |
mov r7, 7 |
mov r8, 8 |
mov r9, 9 |
nop |
mov r10, 10 |
mov r11, 11 |
mov r12, 12 |
mov r13, 13 |
mov r14, 14 |
mov r15, 15 |
mov r16, 16 |
mov r17, 17 |
mov r18, 18 |
mov r19, 19 |
mov r20, 20 |
mov r21, 21 |
mov r22, 22 |
mov r23, 23 |
mov r24, 24 |
mov r25, 25 |
mov r26, 26 |
mov r27, 27 |
mov r28, 28 |
mov r29, 29 |
mov r30, 30 |
mov r31, 31 |
mov r32, 32 |
mov r33, 33 |
mov r34, 34 |
mov r35, 35 |
mov r36, 36 |
mov r37, 37 |
mov r38, 38 |
mov r39, 39 |
mov r40, 40 |
mov r41, 41 |
mov r42, 42 |
mov r43, 43 |
mov r44, 44 |
mov r45, 45 |
mov r46, 46 |
mov r47, 47 |
mov r48, 48 |
mov r49, 49 |
mov r50, 50 |
mov r51, 51 |
mov r52, 52 |
mov r53, 53 |
mov r54, 54 |
mov r55, 55 |
mov r56, 56 |
mov r57, 57 |
mov r58, 58 |
mov r59, 59 |
mov r60, 60 |
mov r61, 61 |
mov r62, 62 |
mov r63, 63 |
mov r64, 64 |
mov r65, 65 |
mov r66, 66 |
mov r67, 67 |
mov r68, 68 |
mov r69, 69 |
mov r70, 70 |
mov r71, 71 |
mov r72, 72 |
mov r73, 73 |
mov r74, 74 |
mov r75, 75 |
mov r76, 76 |
mov r77, 77 |
mov r78, 78 |
mov r79, 79 |
mov r80, 80 |
mov r81, 81 |
mov r82, 82 |
mov r83, 83 |
mov r84, 84 |
mov r85, 85 |
mov r86, 86 |
mov r87, 87 |
mov r88, 88 |
mov r89, 89 |
mov r90, 90 |
mov r91, 91 |
mov r92, 92 |
mov r93, 93 |
mov r94, 94 |
mov r95, 95 |
mov r96, 96 |
mov r97, 97 |
mov r98, 98 |
mov r99, 99 |
mov r100, 0 |
mov r101, 1 |
mov r102, 2 |
mov r103, 3 |
mov r104, 4 |
mov r105, 5 |
mov r106, 6 |
mov r107, 7 |
mov r108, 8 |
mov r109, 9 |
mov r110, 10 |
mov r111, 11 |
mov r112, 12 |
mov r113, 13 |
mov r114, 14 |
mov r115, 15 |
mov r116, 16 |
mov r117, 17 |
mov r118, 18 |
mov r119, 19 |
mov r120, 20 |
mov r121, 21 |
mov r122, 22 |
mov r123, 23 |
mov r124, 24 |
mov r125, 25 |
mov r126, 26 |
mov r127, 27 |
mov r128, 28 |
mov r129, 29 |
mov r130, 30 |
mov r131, 31 |
mov r132, 32 |
mov r133, 33 |
mov r134, 34 |
mov r135, 35 |
mov r136, 36 |
mov r137, 37 |
mov r138, 38 |
mov r139, 39 |
mov r140, 40 |
mov r141, 41 |
mov r142, 42 |
mov r143, 43 |
mov r144, 44 |
mov r145, 45 |
mov r146, 46 |
mov r147, 47 |
mov r148, 48 |
mov r149, 49 |
mov r150, 50 |
mov r151, 51 |
mov r152, 52 |
mov r153, 53 |
mov r154, 54 |
mov r155, 55 |
mov r156, 56 |
mov r157, 57 |
mov r158, 58 |
mov r159, 59 |
mov r160, 60 |
mov r161, 61 |
mov r162, 62 |
mov r163, 63 |
mov r164, 64 |
mov r165, 65 |
mov r166, 66 |
mov r167, 67 |
mov r168, 68 |
mov r169, 69 |
mov r170, 70 |
mov r171, 71 |
mov r172, 72 |
mov r173, 73 |
mov r174, 74 |
mov r175, 75 |
mov r176, 76 |
mov r177, 77 |
mov r178, 78 |
mov r179, 79 |
mov r180, 80 |
mov r181, 81 |
mov r182, 82 |
mov r183, 83 |
mov r184, 84 |
mov r185, 85 |
mov r186, 86 |
mov r187, 87 |
mov r188, 88 |
mov r189, 89 |
mov r190, 90 |
mov r191, 91 |
mov r192, 92 |
mov r193, 93 |
mov r194, 94 |
mov r195, 95 |
mov r196, 96 |
mov r197, 97 |
mov r198, 98 |
mov r199, 99 |
mov r200, 0 |
mov r201, 1 |
mov r202, 2 |
mov r203, 3 |
mov r204, 4 |
mov r205, 5 |
mov r206, 6 |
mov r207, 7 |
mov r208, 8 |
mov r209, 9 |
mov r210, 10 |
mov r211, 11 |
mov r212, 12 |
mov r213, 13 |
mov r214, 14 |
mov r215, 15 |
mov r216, 16 |
mov r217, 17 |
mov r218, 18 |
mov r219, 19 |
mov r220, 20 |
mov r221, 21 |
mov r222, 22 |
mov r223, 23 |
mov r224, 24 |
mov r225, 25 |
mov r226, 26 |
mov r227, 27 |
mov r228, 28 |
mov r229, 29 |
mov r230, 30 |
mov r231, 31 |
mov r232, 32 |
mov r233, 33 |
mov r234, 34 |
mov r235, 35 |
mov r236, 36 |
mov r237, 37 |
mov r238, 38 |
mov r239, 39 |
mov r240, 40 |
mov r241, 41 |
mov r242, 42 |
mov r243, 43 |
mov r244, 44 |
mov r245, 45 |
mov r246, 46 |
mov r247, 47 |
mov r248, 48 |
mov r249, 49 |
mov r250, 50 |
mov r251, 51 |
mov r252, 52 |
mov r253, 53 |
mov r254, 54 |
mov r255, 55 |
|
nop |
nop |
nop |
|
cjmpne r0, r1, 1 |
nop |
cjmpne r0, r2, 2 |
nop |
nop |
cjmpne r0, r3, 3 |
cjmpne r0, r4, 4 |
cjmpne r0, r5, 5 |
nop |
cjmpne r0, r6, 6 |
cjmpne r0, r7, 7 |
cjmpne r0, r8, 8 |
cjmpne r0, r9, 9 |
cjmpne r0, r10, 10 |
cjmpne r0, r11, 11 |
nop |
nop |
nop |
cjmpne r0, r12, 12 |
cjmpne r0, r13, 13 |
cjmpne r0, r14, 14 |
cjmpne r0, r15, 15 |
cjmpne r0, r16, 16 |
cjmpne r0, r17, 17 |
cjmpne r0, r18, 18 |
cjmpne r0, r19, 19 |
cjmpne r0, r20, 20 |
cjmpne r0, r21, 21 |
cjmpne r0, r22, 22 |
cjmpne r0, r23, 23 |
cjmpne r0, r24, 24 |
cjmpne r0, r25, 25 |
cjmpne r0, r26, 26 |
cjmpne r0, r27, 27 |
cjmpne r0, r28, 28 |
cjmpne r0, r29, 29 |
cjmpne r0, r30, 30 |
cjmpne r0, r31, 31 |
cjmpne r0, r32, 32 |
cjmpne r0, r33, 33 |
cjmpne r0, r34, 34 |
cjmpne r0, r35, 35 |
cjmpne r0, r36, 36 |
cjmpne r0, r37, 37 |
cjmpne r0, r38, 38 |
cjmpne r0, r39, 39 |
cjmpne r0, r40, 40 |
cjmpne r0, r41, 41 |
cjmpne r0, r42, 42 |
cjmpne r0, r43, 43 |
cjmpne r0, r44, 44 |
cjmpne r0, r45, 45 |
cjmpne r0, r46, 46 |
cjmpne r0, r47, 47 |
cjmpne r0, r48, 48 |
cjmpne r0, r49, 49 |
cjmpne r0, r50, 50 |
cjmpne r0, r51, 51 |
cjmpne r0, r52, 52 |
cjmpne r0, r53, 53 |
cjmpne r0, r54, 54 |
cjmpne r0, r55, 55 |
cjmpne r0, r56, 56 |
cjmpne r0, r57, 57 |
cjmpne r0, r58, 58 |
cjmpne r0, r59, 59 |
cjmpne r0, r60, 60 |
cjmpne r0, r61, 61 |
cjmpne r0, r62, 62 |
cjmpne r0, r63, 63 |
cjmpne r0, r64, 64 |
cjmpne r0, r65, 65 |
cjmpne r0, r66, 66 |
cjmpne r0, r67, 67 |
cjmpne r0, r68, 68 |
cjmpne r0, r69, 69 |
cjmpne r0, r70, 70 |
cjmpne r0, r71, 71 |
cjmpne r0, r72, 72 |
cjmpne r0, r73, 73 |
cjmpne r0, r74, 74 |
cjmpne r0, r75, 75 |
cjmpne r0, r76, 76 |
cjmpne r0, r77, 77 |
cjmpne r0, r78, 78 |
cjmpne r0, r79, 79 |
cjmpne r0, r80, 80 |
cjmpne r0, r81, 81 |
cjmpne r0, r82, 82 |
cjmpne r0, r83, 83 |
cjmpne r0, r84, 84 |
cjmpne r0, r85, 85 |
cjmpne r0, r86, 86 |
cjmpne r0, r87, 87 |
cjmpne r0, r88, 88 |
cjmpne r0, r89, 89 |
cjmpne r0, r90, 90 |
cjmpne r0, r91, 91 |
cjmpne r0, r92, 92 |
cjmpne r0, r93, 93 |
cjmpne r0, r94, 94 |
cjmpne r0, r95, 95 |
cjmpne r0, r96, 96 |
cjmpne r0, r97, 97 |
cjmpne r0, r98, 98 |
cjmpne r0, r99, 99 |
cjmpne r0, r100, 0 |
cjmpne r0, r101, 1 |
cjmpne r0, r102, 2 |
cjmpne r0, r103, 3 |
cjmpne r0, r104, 4 |
cjmpne r0, r105, 5 |
cjmpne r0, r106, 6 |
cjmpne r0, r107, 7 |
cjmpne r0, r108, 8 |
cjmpne r0, r109, 9 |
cjmpne r0, r110, 10 |
cjmpne r0, r111, 11 |
cjmpne r0, r112, 12 |
cjmpne r0, r113, 13 |
cjmpne r0, r114, 14 |
cjmpne r0, r115, 15 |
cjmpne r0, r116, 16 |
cjmpne r0, r117, 17 |
cjmpne r0, r118, 18 |
cjmpne r0, r119, 19 |
cjmpne r0, r120, 20 |
cjmpne r0, r121, 21 |
cjmpne r0, r122, 22 |
cjmpne r0, r123, 23 |
cjmpne r0, r124, 24 |
cjmpne r0, r125, 25 |
cjmpne r0, r126, 26 |
cjmpne r0, r127, 27 |
cjmpne r0, r128, 28 |
cjmpne r0, r129, 29 |
cjmpne r0, r130, 30 |
cjmpne r0, r131, 31 |
cjmpne r0, r132, 32 |
cjmpne r0, r133, 33 |
cjmpne r0, r134, 34 |
cjmpne r0, r135, 35 |
cjmpne r0, r136, 36 |
cjmpne r0, r137, 37 |
cjmpne r0, r138, 38 |
cjmpne r0, r139, 39 |
cjmpne r0, r140, 40 |
cjmpne r0, r141, 41 |
cjmpne r0, r142, 42 |
cjmpne r0, r143, 43 |
cjmpne r0, r144, 44 |
cjmpne r0, r145, 45 |
cjmpne r0, r146, 46 |
cjmpne r0, r147, 47 |
cjmpne r0, r148, 48 |
cjmpne r0, r149, 49 |
cjmpne r0, r150, 50 |
cjmpne r0, r151, 51 |
cjmpne r0, r152, 52 |
cjmpne r0, r153, 53 |
cjmpne r0, r154, 54 |
cjmpne r0, r155, 55 |
cjmpne r0, r156, 56 |
cjmpne r0, r157, 57 |
cjmpne r0, r158, 58 |
cjmpne r0, r159, 59 |
cjmpne r0, r160, 60 |
cjmpne r0, r161, 61 |
cjmpne r0, r162, 62 |
cjmpne r0, r163, 63 |
cjmpne r0, r164, 64 |
cjmpne r0, r165, 65 |
cjmpne r0, r166, 66 |
cjmpne r0, r167, 67 |
cjmpne r0, r168, 68 |
cjmpne r0, r169, 69 |
cjmpne r0, r170, 70 |
cjmpne r0, r171, 71 |
cjmpne r0, r172, 72 |
cjmpne r0, r173, 73 |
cjmpne r0, r174, 74 |
cjmpne r0, r175, 75 |
cjmpne r0, r176, 76 |
cjmpne r0, r177, 77 |
cjmpne r0, r178, 78 |
cjmpne r0, r179, 79 |
cjmpne r0, r180, 80 |
cjmpne r0, r181, 81 |
cjmpne r0, r182, 82 |
cjmpne r0, r183, 83 |
cjmpne r0, r184, 84 |
cjmpne r0, r185, 85 |
cjmpne r0, r186, 86 |
cjmpne r0, r187, 87 |
cjmpne r0, r188, 88 |
cjmpne r0, r189, 89 |
cjmpne r0, r190, 90 |
cjmpne r0, r191, 91 |
cjmpne r0, r192, 92 |
cjmpne r0, r193, 93 |
cjmpne r0, r194, 94 |
cjmpne r0, r195, 95 |
cjmpne r0, r196, 96 |
cjmpne r0, r197, 97 |
cjmpne r0, r198, 98 |
cjmpne r0, r199, 99 |
cjmpne r0, r200, 0 |
cjmpne r0, r201, 1 |
cjmpne r0, r202, 2 |
cjmpne r0, r203, 3 |
cjmpne r0, r204, 4 |
cjmpne r0, r205, 5 |
cjmpne r0, r206, 6 |
cjmpne r0, r207, 7 |
cjmpne r0, r208, 8 |
cjmpne r0, r209, 9 |
cjmpne r0, r210, 10 |
cjmpne r0, r211, 11 |
cjmpne r0, r212, 12 |
cjmpne r0, r213, 13 |
cjmpne r0, r214, 14 |
cjmpne r0, r215, 15 |
cjmpne r0, r216, 16 |
cjmpne r0, r217, 17 |
cjmpne r0, r218, 18 |
cjmpne r0, r219, 19 |
cjmpne r0, r220, 20 |
cjmpne r0, r221, 21 |
cjmpne r0, r222, 22 |
cjmpne r0, r223, 23 |
cjmpne r0, r224, 24 |
cjmpne r0, r225, 25 |
cjmpne r0, r226, 26 |
cjmpne r0, r227, 27 |
cjmpne r0, r228, 28 |
cjmpne r0, r229, 29 |
cjmpne r0, r230, 30 |
cjmpne r0, r231, 31 |
cjmpne r0, r232, 32 |
cjmpne r0, r233, 33 |
cjmpne r0, r234, 34 |
cjmpne r0, r235, 35 |
cjmpne r0, r236, 36 |
cjmpne r0, r237, 37 |
cjmpne r0, r238, 38 |
cjmpne r0, r239, 39 |
cjmpne r0, r240, 40 |
cjmpne r0, r241, 41 |
cjmpne r0, r242, 42 |
cjmpne r0, r243, 43 |
cjmpne r0, r244, 44 |
cjmpne r0, r245, 45 |
cjmpne r0, r246, 46 |
cjmpne r0, r247, 47 |
cjmpne r0, r248, 48 |
cjmpne r0, r249, 49 |
cjmpne r0, r250, 50 |
cjmpne r0, r251, 51 |
cjmpne r0, r252, 52 |
cjmpne r0, r253, 53 |
cjmpne r0, r254, 54 |
cjmpne r0, r255, 55 |
|
lc r100, 0x10000000 |
lc r101, halt |
sw r100, 1 |
jmp r101 |
|
failure: |
lc r100, 0x10000000 |
lc r101, halt |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test013.asm
0,0 → 1,935
/* |
* Test division (divu, divs, modu, mods) |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
lc r103, 0x10000004 |
|
// Test unsigned division |
|
lc r32, op1 |
lc r33, op2 |
lc r34, quotient |
lc r35, remainder |
lc r36, cnt_loop |
lc r10, 100 // counter |
|
cnt_loop: |
lw r0, r32 |
lw r1, r33 |
divu r2, r0, r1 |
modu r3, r0, r1 |
lw r4, r34 |
lw r5, r35 |
cjmpne r102, r2, r4 // failure |
cjmpne r102, r3, r5 // failure |
mul r6, r2, r1 |
add r6, r6, r3 |
cjmpne r102, r6, r0 // failure |
add r32, r32, 4 |
add r33, r33, 4 |
add r34, r34, 4 |
add r35, r35, 4 |
sub r10, r10, 1 |
cjmpug r36, r10, 0 // cnt_loop |
|
sw r103, 1 |
|
// Test signed division |
|
lc r32, op1_signed |
lc r33, op2_signed |
lc r34, quotient_signed |
lc r35, remainder_signed |
lc r36, cnt_loop2 |
lc r10, 100 // counter |
|
cnt_loop2: |
lw r0, r32 |
lw r1, r33 |
divs r2, r0, r1 |
mods r3, r0, r1 |
lw r4, r34 |
lw r5, r35 |
cjmpne r102, r2, r4 // failure |
cjmpne r102, r3, r5 // failure |
mul r6, r2, r1 |
add r6, r6, r3 |
cjmpne r102, r6, r0 // failure |
add r32, r32, 4 |
add r33, r33, 4 |
add r34, r34, 4 |
add r35, r35, 4 |
sub r10, r10, 1 |
cjmpug r36, r10, 0 // cnt_loop2 |
|
sw r103, 2 |
|
// Random division/multiplication test |
|
mov r64, 1 // initial PRBS value |
lc r65, 1103515245 // PRBS multiplier |
lc r66, 12345 // PRBS addition constant |
lc r67, 32767 // PRBS mask |
lc r68, 16384 |
|
lc r10, 1000 |
lc r32, rnd_loop |
lc r33, rand |
lc r34, rnd_cont |
|
rnd_loop: |
call r33 // rand |
sub r1, r0, r68 // dividend in r1 |
call r33 // rand |
sub r2, r0, r68 // divisor in r2 |
cjmpne r34, r2, 0 // rnd_cont |
mov r2, 1 |
|
rnd_cont: |
divs r3, r1, r2 |
mods r4, r1, r2 |
mul r5, r3, r2 |
add r5, r5, r4 |
cjmpne r102, r5, r1 // failure |
sub r10, r10, 1 |
cjmpug r32, r10, 0 // rnd_loop |
|
sw r103, 3 |
|
sw r100, 1 |
jmp r101 // halt |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 // halt |
|
// Linear congruent pseudo-random number generator (as in ISO/IEC 9899:1999) |
|
rand: |
mul r64, r64, r65 |
add r64, r64, r66 |
sru r0, r64, 16 |
and r0, r0, r67 |
ret |
|
op1: |
.word 2227053353 |
.word 4059122064 |
.word 210189531 |
.word 1203176988 |
.word 2794175299 |
.word 1562322232 |
.word 219364165 |
.word 1352278066 |
.word 4130490642 |
.word 1156715599 |
.word 993179440 |
.word 529260957 |
.word 1235055289 |
.word 2994792917 |
.word 348116583 |
.word 1475314534 |
.word 517066081 |
.word 2230328806 |
.word 1395407336 |
.word 4094467700 |
.word 3476389465 |
.word 1210945747 |
.word 3236243997 |
.word 1348406852 |
.word 118818171 |
.word 1692045936 |
.word 1190663529 |
.word 1731139289 |
.word 1986549712 |
.word 2038965422 |
.word 1173634277 |
.word 1499514357 |
.word 3268423030 |
.word 136673642 |
.word 672245098 |
.word 797742983 |
.word 3236010945 |
.word 1421197958 |
.word 948983249 |
.word 3780009574 |
.word 3802522271 |
.word 2303076920 |
.word 2976105080 |
.word 2531287614 |
.word 4049504575 |
.word 488644257 |
.word 336561159 |
.word 753386953 |
.word 4270696691 |
.word 1966854304 |
.word 3201322355 |
.word 4203490113 |
.word 2503116372 |
.word 3405118694 |
.word 3103595329 |
.word 3553466644 |
.word 861103782 |
.word 1275325516 |
.word 3484264974 |
.word 1293196760 |
.word 4173438076 |
.word 2275850340 |
.word 3885575502 |
.word 1091087744 |
.word 2669208735 |
.word 69325132 |
.word 2319663187 |
.word 2510410703 |
.word 2902980138 |
.word 845791433 |
.word 3327530895 |
.word 948013067 |
.word 3889190137 |
.word 2250058130 |
.word 927988252 |
.word 1820385269 |
.word 135357763 |
.word 3770160619 |
.word 1650193531 |
.word 1250420215 |
.word 2982397572 |
.word 788754293 |
.word 1132578971 |
.word 2174830494 |
.word 2743197342 |
.word 1473236291 |
.word 671295260 |
.word 1615737929 |
.word 722317890 |
.word 1373795119 |
.word 3187744723 |
.word 353993505 |
.word 3691968907 |
.word 2452722797 |
.word 1784599952 |
.word 912987979 |
.word 2183033578 |
.word 2941180254 |
.word 3706025245 |
.word 2141225307 |
|
op2: |
.word 225 |
.word 3018582459 |
.word 6462 |
.word 222 |
.word 799 |
.word 19412 |
.word 3676048009 |
.word 16622 |
.word 32158 |
.word 64 |
.word 8865 |
.word 2580197594 |
.word 3313401900 |
.word 1405929962 |
.word 214 |
.word 24905 |
.word 1914084509 |
.word 41372239 |
.word 203 |
.word 213 |
.word 50 |
.word 3866464211 |
.word 247 |
.word 2371998720 |
.word 528626491 |
.word 2243022420 |
.word 106 |
.word 21608 |
.word 250 |
.word 635130622 |
.word 22739 |
.word 61 |
.word 3563638217 |
.word 97 |
.word 2849410241 |
.word 241 |
.word 2594761086 |
.word 191 |
.word 40 |
.word 48 |
.word 6924 |
.word 2054783657 |
.word 702209830 |
.word 968832018 |
.word 1826819693 |
.word 17099 |
.word 25861 |
.word 142 |
.word 190 |
.word 3078 |
.word 130 |
.word 32560 |
.word 170 |
.word 2308931006 |
.word 26703 |
.word 13 |
.word 19211 |
.word 115 |
.word 667594965 |
.word 1495759348 |
.word 205 |
.word 19605 |
.word 61 |
.word 2772527081 |
.word 16917 |
.word 147 |
.word 175 |
.word 206 |
.word 16033 |
.word 2356 |
.word 7643 |
.word 97 |
.word 2017990513 |
.word 41 |
.word 13752 |
.word 109 |
.word 165 |
.word 225 |
.word 2698 |
.word 12859 |
.word 12455 |
.word 158 |
.word 4136249385 |
.word 3446419784 |
.word 140 |
.word 20679 |
.word 23935 |
.word 48162108 |
.word 5302 |
.word 126 |
.word 80 |
.word 741054488 |
.word 188 |
.word 116 |
.word 64 |
.word 177 |
.word 141 |
.word 16378 |
.word 3662043152 |
.word 19292 |
|
quotient: |
.word 9898014 |
.word 1 |
.word 32527 |
.word 5419716 |
.word 3497090 |
.word 80482 |
.word 0 |
.word 81354 |
.word 128443 |
.word 18073681 |
.word 112033 |
.word 0 |
.word 0 |
.word 2 |
.word 1626713 |
.word 59237 |
.word 0 |
.word 53 |
.word 6873927 |
.word 19222853 |
.word 69527789 |
.word 0 |
.word 13102202 |
.word 0 |
.word 0 |
.word 0 |
.word 11232674 |
.word 80115 |
.word 7946198 |
.word 3 |
.word 51613 |
.word 24582202 |
.word 0 |
.word 1409006 |
.word 0 |
.word 3310136 |
.word 1 |
.word 7440827 |
.word 23724581 |
.word 78750199 |
.word 549179 |
.word 1 |
.word 4 |
.word 2 |
.word 2 |
.word 28577 |
.word 13014 |
.word 5305541 |
.word 22477351 |
.word 639003 |
.word 24625556 |
.word 129099 |
.word 14724213 |
.word 1 |
.word 116226 |
.word 273343588 |
.word 44823 |
.word 11089787 |
.word 5 |
.word 0 |
.word 20358234 |
.word 116085 |
.word 63697959 |
.word 0 |
.word 157782 |
.word 471599 |
.word 13255218 |
.word 12186459 |
.word 181062 |
.word 358994 |
.word 435369 |
.word 9773330 |
.word 1 |
.word 54879466 |
.word 67480 |
.word 16700782 |
.word 820350 |
.word 16756269 |
.word 611635 |
.word 97240 |
.word 239453 |
.word 4992115 |
.word 0 |
.word 0 |
.word 19594266 |
.word 71243 |
.word 28046 |
.word 33 |
.word 136234 |
.word 10903135 |
.word 39846809 |
.word 0 |
.word 19638132 |
.word 21144162 |
.word 27884374 |
.word 5158124 |
.word 15482507 |
.word 179581 |
.word 1 |
.word 110990 |
|
remainder: |
.word 203 |
.word 1040539605 |
.word 57 |
.word 36 |
.word 389 |
.word 5648 |
.word 219364165 |
.word 11878 |
.word 20648 |
.word 15 |
.word 6895 |
.word 529260957 |
.word 1235055289 |
.word 182932993 |
.word 1 |
.word 17049 |
.word 517066081 |
.word 37600139 |
.word 155 |
.word 11 |
.word 15 |
.word 1210945747 |
.word 103 |
.word 1348406852 |
.word 118818171 |
.word 1692045936 |
.word 85 |
.word 14369 |
.word 212 |
.word 133573556 |
.word 6270 |
.word 35 |
.word 3268423030 |
.word 60 |
.word 672245098 |
.word 207 |
.word 641249859 |
.word 1 |
.word 9 |
.word 22 |
.word 6875 |
.word 248293263 |
.word 167265760 |
.word 593623578 |
.word 395865189 |
.word 6134 |
.word 6105 |
.word 131 |
.word 1 |
.word 3070 |
.word 75 |
.word 26673 |
.word 162 |
.word 1096187688 |
.word 12451 |
.word 0 |
.word 9129 |
.word 11 |
.word 146290149 |
.word 1293196760 |
.word 106 |
.word 3915 |
.word 3 |
.word 1091087744 |
.word 10641 |
.word 79 |
.word 37 |
.word 149 |
.word 13092 |
.word 1569 |
.word 5628 |
.word 57 |
.word 1871199624 |
.word 24 |
.word 3292 |
.word 31 |
.word 13 |
.word 94 |
.word 2301 |
.word 11055 |
.word 10457 |
.word 123 |
.word 1132578971 |
.word 2174830494 |
.word 102 |
.word 2294 |
.word 14250 |
.word 26388365 |
.word 5222 |
.word 109 |
.word 3 |
.word 353993505 |
.word 91 |
.word 5 |
.word 16 |
.word 31 |
.word 91 |
.word 2636 |
.word 43982093 |
.word 6227 |
|
op1_signed: |
.word 1173464398 |
.word 644568570 |
.word 1413618866 |
.word 940280095 |
.word 1307051796 |
.word 69701148 |
.word 791353789 |
.word -1751134801 |
.word -540034563 |
.word -664201053 |
.word 859052625 |
.word 506263265 |
.word 1672805452 |
.word -940950084 |
.word 639564287 |
.word -320080770 |
.word -194326606 |
.word -1401122692 |
.word 1361841711 |
.word -1572666822 |
.word 223085807 |
.word -2143536785 |
.word -771364638 |
.word -392756254 |
.word -2075946315 |
.word -133598861 |
.word 869612982 |
.word 727395029 |
.word -1173738546 |
.word 1865699269 |
.word 1001660457 |
.word -1435705417 |
.word 313397375 |
.word 91734875 |
.word 55211040 |
.word -1298145437 |
.word -1587928274 |
.word 120185203 |
.word 1253220522 |
.word 664380448 |
.word 659766337 |
.word -1867423126 |
.word 211715071 |
.word 1172375319 |
.word 1010876232 |
.word 1866163921 |
.word 1337698510 |
.word -1489886717 |
.word -844206754 |
.word 1252556476 |
.word 1062583479 |
.word -2028701144 |
.word -925730358 |
.word 63629404 |
.word 2084388372 |
.word 1185701000 |
.word 344972780 |
.word 1506745295 |
.word -1310164994 |
.word 785548626 |
.word -960828075 |
.word -788757195 |
.word 1742449807 |
.word 1952581789 |
.word -1868879050 |
.word -727870971 |
.word 457544035 |
.word -2083100074 |
.word 2092326142 |
.word 456912800 |
.word -1930624925 |
.word 1981026677 |
.word -641082819 |
.word 1259278117 |
.word 1481501124 |
.word -444342667 |
.word 1947675341 |
.word -608834426 |
.word -906130612 |
.word -480045052 |
.word 182898482 |
.word 1025708506 |
.word -363535658 |
.word 1180470009 |
.word -62562240 |
.word 987486196 |
.word -531865065 |
.word 676720261 |
.word 1125242878 |
.word -1621168845 |
.word 1990517921 |
.word -1383494740 |
.word -1522980151 |
.word 434249114 |
.word -129245145 |
.word 97983477 |
.word 658513595 |
.word 1548110625 |
.word 1140579073 |
.word -1285950881 |
|
op2_signed: |
.word 1389891600 |
.word 176 |
.word -129 |
.word -10300 |
.word -24713 |
.word 36 |
.word 539501672 |
.word -412262764 |
.word -999517400 |
.word 27 |
.word -185 |
.word 632355259 |
.word 1914747195 |
.word -202 |
.word 152 |
.word -151 |
.word -49343821 |
.word 161975794 |
.word -172 |
.word -3509 |
.word 8811 |
.word -135 |
.word -224114196 |
.word 5373 |
.word -30158 |
.word 735955126 |
.word 7320 |
.word -1137550910 |
.word 413980723 |
.word -28499 |
.word -1858419248 |
.word -31374 |
.word 153 |
.word -245 |
.word -231 |
.word 1065580722 |
.word 114 |
.word 25851 |
.word 1113949699 |
.word 1883394154 |
.word 211 |
.word 22075 |
.word -143 |
.word -1052684430 |
.word -197 |
.word -28563 |
.word -12517 |
.word 141 |
.word -197 |
.word -79 |
.word 79 |
.word -103 |
.word 563090517 |
.word -13711 |
.word -2 |
.word -250 |
.word -130626268 |
.word -1824933506 |
.word 501 |
.word 135 |
.word -965460037 |
.word 90 |
.word -24983 |
.word -5626 |
.word -175 |
.word -42 |
.word -72 |
.word 9948 |
.word -29411 |
.word 223 |
.word 16237 |
.word -192 |
.word -297352066 |
.word 2140975764 |
.word -511619965 |
.word -7170 |
.word -380546211 |
.word 1065203866 |
.word -81584403 |
.word -23445 |
.word 213 |
.word 31410 |
.word -270351532 |
.word -8040 |
.word -13293 |
.word 16010 |
.word -223 |
.word -60 |
.word 9338 |
.word -46 |
.word 26045 |
.word 21525 |
.word 1705744137 |
.word 1754448529 |
.word -1602585578 |
.word 1341015177 |
.word 1 |
.word -72 |
.word -156 |
.word 1932634409 |
|
quotient_signed: |
.word 0 |
.word 3662321 |
.word -10958285 |
.word -91289 |
.word -52889 |
.word 1936143 |
.word 1 |
.word 4 |
.word 0 |
.word -24600039 |
.word -4643527 |
.word 0 |
.word 0 |
.word 4658168 |
.word 4207659 |
.word 2119740 |
.word 3 |
.word -8 |
.word -7917684 |
.word 448180 |
.word 25319 |
.word 15878050 |
.word 3 |
.word -73098 |
.word 68835 |
.word 0 |
.word 118799 |
.word 0 |
.word -2 |
.word -65465 |
.word 0 |
.word 45760 |
.word 2048348 |
.word -374428 |
.word -239008 |
.word -1 |
.word -13929195 |
.word 4649 |
.word 1 |
.word 0 |
.word 3126854 |
.word -84594 |
.word -1480524 |
.word -1 |
.word -5131351 |
.word -65335 |
.word -106870 |
.word -10566572 |
.word 4285313 |
.word -15855145 |
.word 13450423 |
.word 19696127 |
.word -1 |
.word -4640 |
.word -1042194186 |
.word -4742804 |
.word -2 |
.word 0 |
.word -2615099 |
.word 5818878 |
.word 0 |
.word -8763968 |
.word -69745 |
.word -347063 |
.word 10679308 |
.word 17330261 |
.word -6354778 |
.word -209398 |
.word -71140 |
.word 2048936 |
.word -118902 |
.word -10317847 |
.word 2 |
.word 0 |
.word -2 |
.word 61972 |
.word -5 |
.word 0 |
.word 11 |
.word 20475 |
.word 858678 |
.word 32655 |
.word 1 |
.word -146824 |
.word 4706 |
.word 61679 |
.word 2385045 |
.word -11278671 |
.word 120501 |
.word 35242800 |
.word 76426 |
.word -64273 |
.word 0 |
.word 0 |
.word 0 |
.word 0 |
.word 658513595 |
.word -21501536 |
.word -7311404 |
.word 0 |
|
remainder_signed: |
.word 1173464398 |
.word 74 |
.word 101 |
.word 3395 |
.word 5939 |
.word 0 |
.word 251852117 |
.word -102083745 |
.word -540034563 |
.word 0 |
.word 130 |
.word 506263265 |
.word 1672805452 |
.word -148 |
.word 119 |
.word -30 |
.word -46295143 |
.word -105316340 |
.word 63 |
.word -3202 |
.word 98 |
.word -35 |
.word -99022050 |
.word -700 |
.word -20385 |
.word -133598861 |
.word 4302 |
.word 727395029 |
.word -345777100 |
.word 12234 |
.word 1001660457 |
.word -31177 |
.word 131 |
.word 15 |
.word 192 |
.word -232564715 |
.word -44 |
.word 3904 |
.word 139270823 |
.word 664380448 |
.word 143 |
.word -10576 |
.word 139 |
.word 119690889 |
.word 85 |
.word 316 |
.word 6720 |
.word -65 |
.word -93 |
.word 21 |
.word 62 |
.word -63 |
.word -362639841 |
.word 10364 |
.word 0 |
.word 0 |
.word 83720244 |
.word 1506745295 |
.word -395 |
.word 96 |
.word -960828075 |
.word -75 |
.word 10472 |
.word 5351 |
.word -150 |
.word -9 |
.word 19 |
.word -8770 |
.word 27602 |
.word 72 |
.word -13151 |
.word 53 |
.word -46378687 |
.word 1259278117 |
.word 458261194 |
.word -3427 |
.word 44944286 |
.word -608834426 |
.word -8702179 |
.word -8677 |
.word 68 |
.word 14956 |
.word -93184126 |
.word 5049 |
.word -5382 |
.word 5406 |
.word -30 |
.word 1 |
.word 4540 |
.word -45 |
.word 2751 |
.word -18415 |
.word -1522980151 |
.word 434249114 |
.word -129245145 |
.word 97983477 |
.word 0 |
.word 33 |
.word 49 |
.word -1285950881 |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test005.asm
0,0 → 1,81
/* |
* This test verifies bytewise DBUS access |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
|
lc r16, 0x10000004 // output pointer |
lc r17, data // input pointer |
|
// Check for bytewise read |
lc r18, 0xbc |
lc r19, 0x9a |
lc r20, 0x78 |
lc r21, 0x56 |
lc r22, 0xffffffbc |
lc r23, 0xffffff9a |
lc r24, 0x78 |
lc r25, 0x56 |
|
lub r0, r17 |
sw r16, r0 |
cjmpne r102, r0, r18 |
add r17, r17, 1 |
lub r0, r17 |
sw r16, r0 |
cjmpne r102, r0, r19 |
add r17, r17, 1 |
lub r0, r17 |
sw r16, r0 |
cjmpne r102, r0, r20 |
add r17, r17, 1 |
lub r0, r17 |
sw r16, r0 |
cjmpne r102, r0, r21 |
sub r17, r17, 3 |
lsb r0, r17 |
sw r16, r0 |
cjmpne r102, r0, r22 |
add r17, r17, 1 |
lsb r0, r17 |
sw r16, r0 |
cjmpne r102, r0, r23 |
add r17, r17, 1 |
lsb r0, r17 |
sw r16, r0 |
cjmpne r102, r0, r24 |
add r17, r17, 1 |
lsb r0, r17 |
sw r16, r0 |
cjmpne r102, r0, r25 |
|
// Check for bytewise write |
lc r17, 0x00008004 |
sb r17, 0x12 |
add r17, r17, 1 |
sb r17, 0x34 |
add r17, r17, 1 |
sb r17, 0x56 |
add r17, r17, 1 |
sb r17, 0x78 |
|
// Read the whole word and compare |
sub r17, r17, 3 |
lw r0, r17 |
lc r18, 0x78563412 |
cjmpne r102, r0, r18 |
|
sw r100, 1 |
jmp r101 |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 |
|
data: |
.word 0x56789ABC |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test014.asm
0,0 → 1,27
/* |
* Test "hlt" instruction |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r103, 0x20000000 // timer: number of pulses (0xFFFFFFFF - infinite) |
lc r104, 0x20000004 // timer: delay between pulses (in cycles) |
|
lc iv0, timer_handler |
mov r10, 2 |
mov cr, 1 // enable interrupt 0 |
lc r0, 1000 |
sw r104, r0 |
sw r103, 1 |
|
hlt |
|
sw r100, r10 // r10 will be 2 if interrupt hasn't been called, which is a failure code |
|
halt: |
hlt |
jmp r101 // halt |
|
timer_handler: |
mov r10, 1 |
iret |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test006.asm
0,0 → 1,33
/* |
* This test checks for a bug with jump destination register |
* being wrongly overwritten when jump instruction follows "lw" |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
|
lc r16, 0x10000004 |
lc r17, 0x12345678 |
lc r18, 0x12345678 |
|
sw r16, 123 |
lw r0, r16 |
cjmpne r17, 0, 0 // r17 used to be wrongly overwritten by the value of r16 here |
|
sw r16, r17 |
|
nop |
nop |
|
cjmpne r102, r17, r18 |
|
sw r100, 1 |
jmp r101 |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test015.asm
0,0 → 1,59
/* |
* Test unconventional interrupt handlers |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, 0x30000000 // coprocessor input register |
lc r103, 0x30000004 // coprocessor output register |
lc r104, failure |
|
// Initialize interrupt handlers |
lc iv2, coprocessor_handler |
mov cr, 4 // enable interrupts from the coprocessor |
lc r110, interrupt_exit@1 // '1' in the LSB is an interrupt exit flag |
|
// Initialize random generator |
mov r64, 1 // initial PRBS value |
lc r65, 1103515245 // PRBS multiplier |
lc r66, 12345 // PRBS addition constant |
lc r67, 32767 // PRBS mask |
|
// Main loop |
lc r32, loop |
lc r33, rand |
lc r34, 2000 |
|
loop: |
call r33 |
cjmpe r32, r0, 0 // if(r==0) continue; |
sw r102, r0 |
hlt |
|
interrupt_exit: |
lw r1, r103 |
mul r0, r0, 3 |
cjmpne r104, r0, r1 // failure |
|
sub r34, r34, 1 |
cjmpug r32, r34, 0 // loop |
|
sw r100, 1 |
jmp r101 // halt |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 // halt |
|
rand: |
mul r64, r64, r65 |
add r64, r64, r66 |
sru r0, r64, 16 |
and r0, r0, r67 |
ret |
|
coprocessor_handler: |
jmp r110 // exit to a given point, ignore irp |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test007.asm
0,0 → 1,134
/* |
* This test verifies bitwise shift operations. |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
|
lc r16, 0x10000004 // output pointer |
|
// Test left shifts (by comparison with self-addition) |
|
lc r0, 0x12345678 |
mov r3, r0 // for comparison |
lc r32, sl_loop |
mov r1, 0 // counter |
|
sl_loop: |
sl r2, r0, r1 |
sw r16, r2 |
cjmpne r102, r2, r3 |
add r1, r1, 1 |
add r3, r3, r3 |
cjmpul r32, r1, 32 |
|
// Test unsigned right shifts (by comparison with pre-calculated values) |
|
lc r32, sru_loop |
lc r17, sru_expected_data |
mov r1, 0 // counter |
|
sru_loop: |
sru r2, r0, r1 |
sw r16, r2 |
lw r3, r17 |
cjmpne r102, r2, r3 |
add r1, r1, 1 |
add r17, r17, 4 |
cjmpul r32, r1, 32 |
|
// Test signed right shifts (by comparison with pre-calculated values) |
|
lc r0, 0x87654321 |
lc r32, srs_loop |
lc r17, srs_expected_data |
mov r1, 0 // counter |
|
srs_loop: |
srs r2, r0, r1 |
sw r16, r2 |
lw r3, r17 |
cjmpne r102, r2, r3 |
add r1, r1, 1 |
add r17, r17, 4 |
cjmpul r32, r1, 32 |
|
// Report success |
sw r100, 1 |
jmp r101 |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 |
|
sru_expected_data: |
.word 0x12345678 |
.word 0x091A2B3C |
.word 0x048D159E |
.word 0x02468ACF |
.word 0x01234567 |
.word 0x0091A2B3 |
.word 0x0048D159 |
.word 0x002468AC |
.word 0x00123456 |
.word 0x00091A2B |
.word 0x00048D15 |
.word 0x0002468A |
.word 0x00012345 |
.word 0x000091A2 |
.word 0x000048D1 |
.word 0x00002468 |
.word 0x00001234 |
.word 0x0000091A |
.word 0x0000048D |
.word 0x00000246 |
.word 0x00000123 |
.word 0x00000091 |
.word 0x00000048 |
.word 0x00000024 |
.word 0x00000012 |
.word 0x00000009 |
.word 0x00000004 |
.word 0x00000002 |
.word 0x00000001 |
.word 0x00000000 |
.word 0x00000000 |
.word 0x00000000 |
|
srs_expected_data: |
.word 0x87654321 |
.word 0xC3B2A190 |
.word 0xE1D950C8 |
.word 0xF0ECA864 |
.word 0xF8765432 |
.word 0xFC3B2A19 |
.word 0xFE1D950C |
.word 0xFF0ECA86 |
.word 0xFF876543 |
.word 0xFFC3B2A1 |
.word 0xFFE1D950 |
.word 0xFFF0ECA8 |
.word 0xFFF87654 |
.word 0xFFFC3B2A |
.word 0xFFFE1D95 |
.word 0xFFFF0ECA |
.word 0xFFFF8765 |
.word 0xFFFFC3B2 |
.word 0xFFFFE1D9 |
.word 0xFFFFF0EC |
.word 0xFFFFF876 |
.word 0xFFFFFC3B |
.word 0xFFFFFE1D |
.word 0xFFFFFF0E |
.word 0xFFFFFF87 |
.word 0xFFFFFFC3 |
.word 0xFFFFFFE1 |
.word 0xFFFFFFF0 |
.word 0xFFFFFFF8 |
.word 0xFFFFFFFC |
.word 0xFFFFFFFE |
.word 0xFFFFFFFF |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test016.asm
0,0 → 1,47
/* |
* Test for temporarily blocked interrupts |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
lc r103, 0x20000000 // timer: number of pulses (0xFFFFFFFF - infinite) |
lc r104, 0x20000004 // timer: delay between pulses (in cycles) |
|
lc iv0, timer_handler |
lc cr, 0x101 // enable interrupt 0 in temporarily blocked state |
|
lc r32, 0 // interrupt handler call counter |
lc r33, 1000 // loop counter |
lc r34, loop1 |
lc r35, loop2 |
|
sw r104, 100 |
sw r103, 1 |
|
loop1: |
sub r33, r33, 1 |
cjmpug r34, r33, 0 // loop1 |
|
lc r33, 1000 |
mov cr, 1 // unblock interrupt 0 |
|
loop2: |
sub r33, r33, 1 |
cjmpug r35, r33, 0 // loop2 |
|
// r32 should be 1 by this point |
cjmpne r102, r32, 1 // failure |
sw r100, 1 |
jmp r101 // halt |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 // halt |
|
timer_handler: |
add r32, r32, 1 |
iret |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test008.asm
0,0 → 1,59
/* |
* This test calculates a CRC-32 checksum of a small byte array |
* CRC32("123456789")=0xCBF43926 |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
|
lc r16, 0x10000004 // output pointer |
lc r17, 0xFFFFFFFF // initial CRC value |
lc r18, 0xEDB88320 // polynom |
lc r19, data // input pointer |
|
lc r32, byte_loop |
lc r33, bit_loop |
lc r34, dont_xor |
|
mov r20, 0 // byte counter |
|
byte_loop: |
lub r0, r19 |
mov r21, 0 // bit counter |
|
bit_loop: |
and r1, r0, 1 |
and r2, r17, 1 |
sru r17, r17, 1 |
xor r3, r1, r2 |
cjmpe r34, r3, 0 |
xor r17, r17, r18 |
|
dont_xor: |
sru r0, r0, 1 |
add r21, r21, 1 |
cjmpul r33, r21, 8 |
|
add r19, r19, 1 |
add r20, r20, 1 |
cjmpul r32, r20, 9 |
|
not r17, r17 |
sw r16, r17 |
|
lc r0, 0xCBF43926 |
cjmpne r102, r0, r17 |
|
sw r100, 1 |
jmp r101 |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 |
|
data: |
.byte "123456789" |
/lxp32/tags/1.0/verify/lxp32/src/firmware/test009.asm
0,0 → 1,116
/* |
* This test verifies call and ret instructions |
*/ |
|
lc r100, 0x10000000 // test result output pointer |
lc r101, halt |
lc r102, failure |
lc sp, 0x00010000 // stack pointer |
|
lc r0, 0x00008000 |
sw r0, 0 |
|
// Test simple procedure call |
|
lc r1, testproc |
|
call r1 // testproc |
|
lw r0, r0 |
lc r1, 0x11223344 |
|
cjmpne r102, r0, r1 // failure |
|
// Test jump directly to CALL instruction |
lc r1, jump_to_call |
lc r2, testproc2 |
|
jmp r1 |
nop |
nop |
nop |
|
jump_to_call: |
call r2 |
|
lw r0, r0 |
lc r1, 0x55667788 |
|
cjmpne r102, r0, r1 // failure |
|
// Test recursive calls: calculate 10th Fibonnaci number |
// using recursive algorithm |
mov r0, 10 // argument |
mov r16, 0 // how many times test_recursive has been called |
lc r1, test_recursive |
call r1 // test_recursive |
|
lc r1, 0x00008000 |
sw r1, r0 |
|
add r1, r1, 4 |
sw r1, r16 |
|
lc r1, 55 |
cjmpne r102, r0, r1 |
|
lc r1, 177 |
cjmpne r102, r16, r1 |
|
sw r100, 1 |
jmp r101 // halt |
|
failure: |
sw r100, 2 |
|
halt: |
hlt |
jmp r101 // halt |
|
testproc: |
lc r0, 0x00008000 |
lc r1, 0x11223344 |
sw r0, r1 |
ret |
|
testproc2: |
lc r0, 0x00008000 |
lc r1, 0x55667788 |
sw r0, r1 |
ret |
|
test_recursive: |
add r16, r16, 1 // increment call counter |
|
// If r0 is 0 or 1, just return |
cjmpe rp, r0, 0 |
cjmpe rp, r0, 1 |
|
// Save return address in stack |
sub sp, sp, 4 |
sw sp, rp |
// Save argument in stack |
sub sp, sp, 4 |
sw sp, r0 |
// Call itself for with (r0-1) and (r0-2) arguments |
sub r0, r0, 1 |
lc r1, test_recursive |
call r1 |
// Restore value from stack, save temporary result |
lw r1, sp |
sw sp, r0 |
|
sub r0, r1, 2 |
lc r1, test_recursive |
call r1 |
|
// Restore result from stack |
lw r1, sp |
add sp, sp, 4 |
|
add r0, r0, r1 |
|
// Restore return address |
lw rp, sp |
add sp, sp, 4 |
ret |
/lxp32/tags/1.0/verify/lxp32/src/make/sources.make
0,0 → 1,73
# CPU RTL |
|
LXP32_DIR=../../../../rtl |
LXP32_RTL=$(LXP32_DIR)/lxp32_mul16x16.vhd\ |
$(LXP32_DIR)/lxp32_mul_dsp.vhd\ |
$(LXP32_DIR)/lxp32_mul_opt.vhd\ |
$(LXP32_DIR)/lxp32_mul_seq.vhd\ |
$(LXP32_DIR)/lxp32_compl.vhd\ |
$(LXP32_DIR)/lxp32_divider.vhd\ |
$(LXP32_DIR)/lxp32_shifter.vhd\ |
$(LXP32_DIR)/lxp32_alu.vhd\ |
$(LXP32_DIR)/lxp32_dbus.vhd\ |
$(LXP32_DIR)/lxp32_execute.vhd\ |
$(LXP32_DIR)/lxp32_decode.vhd\ |
$(LXP32_DIR)/lxp32_ubuf.vhd\ |
$(LXP32_DIR)/lxp32_fetch.vhd\ |
$(LXP32_DIR)/lxp32_ram256x32.vhd\ |
$(LXP32_DIR)/lxp32_interrupt_mux.vhd\ |
$(LXP32_DIR)/lxp32_scratchpad.vhd\ |
$(LXP32_DIR)/lxp32_cpu.vhd\ |
$(LXP32_DIR)/lxp32u_top.vhd\ |
$(LXP32_DIR)/lxp32_icache.vhd\ |
$(LXP32_DIR)/lxp32c_top.vhd |
|
# Platform RTL |
|
PLATFORM_DIR=../../src/platform |
PLATFORM_RTL=$(PLATFORM_DIR)/generic_dpram.vhd\ |
$(PLATFORM_DIR)/scrambler.vhd\ |
$(PLATFORM_DIR)/dbus_monitor.vhd\ |
$(PLATFORM_DIR)/program_ram.vhd\ |
$(PLATFORM_DIR)/timer.vhd\ |
$(PLATFORM_DIR)/coprocessor.vhd\ |
$(PLATFORM_DIR)/intercon.vhd\ |
$(PLATFORM_DIR)/ibus_adapter.vhd\ |
$(PLATFORM_DIR)/platform.vhd |
|
# Testbench sources |
|
COMMON_PKG_DIR=../../../common_pkg |
TB_DIR=../../src/tb |
TB_SRC=$(COMMON_PKG_DIR)/common_pkg.vhd\ |
$(COMMON_PKG_DIR)/common_pkg_body.vhd\ |
$(TB_DIR)/tb_pkg.vhd\ |
$(TB_DIR)/tb_pkg_body.vhd\ |
$(TB_DIR)/monitor.vhd\ |
$(TB_DIR)/tb.vhd |
|
TB_MOD=tb |
|
# Firmware |
|
FW_SRC_DIR=../../src/firmware |
FIRMWARE=test001.ram\ |
test002.ram\ |
test003.ram\ |
test004.ram\ |
test005.ram\ |
test006.ram\ |
test007.ram\ |
test008.ram\ |
test009.ram\ |
test010.ram\ |
test011.ram\ |
test012.ram\ |
test013.ram\ |
test014.ram\ |
test015.ram\ |
test016.ram |
|
# LXP32 assembler executable |
|
ASM=../../../../tools/bin/lxp32asm |
/lxp32/tags/1.0/verify/common_pkg/common_pkg_body.vhd
0,0 → 1,71
--------------------------------------------------------------------- |
-- Common package for LXP32 testbenches |
-- |
-- Part of the LXP32 verification environment |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
--------------------------------------------------------------------- |
|
use std.textio.all; |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
package body common_pkg is |
impure function rand return integer is |
variable r: unsigned(63 downto 0); |
begin |
r:=rand_state*to_unsigned(1103515245,32)+12345; |
rand_state:=r(rand_state'range); |
return to_integer(rand_state(30 downto 16)); |
end function; |
|
impure function rand(a: integer; b: integer) return integer is |
begin |
assert a<=b report "Invalid range" severity failure; |
return (rand mod (b-a+1))+a; |
end function; |
|
function hex_string(x: std_logic_vector) return string is |
variable xx: std_logic_vector(x'length-1 downto 0); |
variable i: integer:=0; |
variable ii: integer; |
variable c: integer; |
variable s: string(x'length downto 1); |
begin |
xx:=x; |
loop |
ii:=i*4; |
exit when ii>xx'high; |
if ii+3<=xx'high then |
c:=to_integer(unsigned(xx(ii+3 downto ii))); |
else |
c:=to_integer(unsigned(xx(xx'high downto ii))); |
end if; |
|
case c is |
when 0 => s(i+1):='0'; |
when 1 => s(i+1):='1'; |
when 2 => s(i+1):='2'; |
when 3 => s(i+1):='3'; |
when 4 => s(i+1):='4'; |
when 5 => s(i+1):='5'; |
when 6 => s(i+1):='6'; |
when 7 => s(i+1):='7'; |
when 8 => s(i+1):='8'; |
when 9 => s(i+1):='9'; |
when 10 => s(i+1):='A'; |
when 11 => s(i+1):='B'; |
when 12 => s(i+1):='C'; |
when 13 => s(i+1):='D'; |
when 14 => s(i+1):='E'; |
when 15 => s(i+1):='F'; |
when others => s(i+1):='X'; |
end case; |
|
i:=i+1; |
end loop; |
return s(i downto 1); |
end function; |
end package body; |
/lxp32/tags/1.0/verify/common_pkg/common_pkg.vhd
0,0 → 1,24
--------------------------------------------------------------------- |
-- Common package for LXP32 testbenches |
-- |
-- Part of the LXP32 verification environment |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Note: the "rand" function declared in this package implements |
-- a linear congruent pseudo-random number generator as defined in |
-- the ISO/IEC 9899:1999 standard. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
package common_pkg is |
shared variable rand_state: unsigned(31 downto 0):=to_unsigned(1,32); |
|
impure function rand return integer; |
impure function rand(a: integer; b: integer) return integer; |
|
function hex_string(x: std_logic_vector) return string; |
end package; |
/lxp32/tags/1.0/verify/icache/run/xsim/.gitignore
0,0 → 1,10
/.Xil |
/xsim.dir |
webtalk* |
xelab* |
xsim* |
xvhdl* |
hs_err* |
vivado* |
*.stamp |
*.wdb |
/lxp32/tags/1.0/verify/icache/run/xsim/Makefile
0,0 → 1,44
include ../../src/make/sources.make |
|
ifeq ($(findstring Windows,$(OS)),) |
BAT= |
else |
BAT=.bat |
endif |
|
######################## |
# Phony targets |
######################## |
|
all: batch |
|
.PHONY: all compile batch gui clean |
|
compile: compile.stamp |
|
batch: compile.stamp |
xsim$(BAT) -R tb_sim |
|
gui: compile.stamp |
xsim$(BAT) -g -onfinish stop -onerror stop tb_sim |
|
clean: |
rm -rf .Xil |
rm -rf xsim.dir |
rm -f webtalk* |
rm -f xelab* |
rm -f xsim* |
rm -f xvhdl* |
rm -f hs_err* |
rm -f vivado* |
rm -f *.wdb |
rm -f compile.stamp |
|
######################## |
# Normal targets |
######################## |
|
compile.stamp: $(LXP32_RTL) $(TB_SRC) |
xvhdl$(BAT) $(LXP32_RTL) $(TB_SRC) |
xelab$(BAT) work.tb -s tb_sim -debug typical |
echo > compile.stamp |
/lxp32/tags/1.0/verify/icache/run/ghdl/.gitignore
0,0 → 1,5
*.vcd |
*.cf |
*.o |
tb |
compile.stamp |
/lxp32/tags/1.0/verify/icache/run/ghdl/Makefile
0,0 → 1,47
include ../../src/make/sources.make |
|
GHDL_FLAGS=--std=93 |
|
WAVE_VCD=wave.vcd |
WAVE_OUT=wave.fst |
|
######################## |
# Phony targets |
######################## |
|
all: batch |
|
.PHONY: all compile batch gui clean |
|
.PRECIOUS: $(WAVE_OUT) |
|
compile: compile.stamp |
|
batch: compile.stamp |
ghdl -r $(GHDL_FLAGS) $(TB_MOD) |
|
gui: $(WAVE_OUT) |
gtkwave $(WAVE_OUT) |
|
clean: |
rm -f *.cf |
rm -f $(WAVE_VCD) |
rm -f $(WAVE_OUT) |
rm -f *.o |
rm -f $(TB_MOD) |
rm -f compile.stamp |
|
######################## |
# Normal targets |
######################## |
|
$(WAVE_OUT): $(WAVE_VCD) |
vcd2fst $^ $@ |
|
$(WAVE_VCD): compile.stamp |
ghdl -r $(GHDL_FLAGS) $(TB_MOD) --vcd=$(WAVE_VCD) |
|
compile.stamp: $(LXP32_RTL) $(TB_SRC) |
ghdl -a $(GHDL_FLAGS) $(LXP32_RTL) $(TB_SRC) |
ghdl -e $(GHDL_FLAGS) $(TB_MOD) |
echo > compile.stamp |
/lxp32/tags/1.0/verify/icache/run/vsim/.gitignore
0,0 → 1,6
/work |
*.stamp |
*.wlf |
*.ini |
*.o |
transcript |
/lxp32/tags/1.0/verify/icache/run/vsim/Makefile
0,0 → 1,39
include ../../src/make/sources.make |
|
VCOMFLAGS=-93 |
VSIMFLAGS=-t 1ps |
|
######################## |
# Phony targets |
######################## |
|
all: batch |
|
.PHONY: all compile batch gui clean |
|
compile: compile.stamp |
|
batch: compile.stamp |
vsim $(VSIMFLAGS) -do "run -all; quit -f" -c work.$(TB_MOD) |
|
gui: compile.stamp |
vsim $(VSIMFLAGS) work.$(TB_MOD) |
|
clean: |
rm -rf work |
rm -f modelsim.ini |
rm -f transcript |
rm -f vsim.wlf |
rm -f compile.stamp |
|
######################## |
# Normal targets |
######################## |
|
compile.stamp: $(LXP32_RTL) $(TB_SRC) | work |
vcom $(VCOMFLAGS) $(LXP32_RTL) $(TB_SRC) |
echo > compile.stamp |
|
work: |
vlib work |
vmap work work |
/lxp32/tags/1.0/verify/icache/src/tb/ram_model.vhd
0,0 → 1,78
--------------------------------------------------------------------- |
-- RAM model |
-- |
-- Part of the LXP32 instruction cache testbench |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Simulates RAM controller which provides WISHBONE registered |
-- feedback interface. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
|
use work.common_pkg.all; |
use work.tb_pkg.all; |
|
entity ram_model is |
port( |
clk_i: in std_logic; |
|
wbm_cyc_i: in std_logic; |
wbm_stb_i: in std_logic; |
wbm_cti_i: in std_logic_vector(2 downto 0); |
wbm_bte_i: in std_logic_vector(1 downto 0); |
wbm_ack_o: out std_logic; |
wbm_adr_i: in std_logic_vector(29 downto 0); |
wbm_dat_o: out std_logic_vector(31 downto 0) |
); |
end entity; |
|
architecture sim of ram_model is |
|
signal ack: std_logic:='0'; |
signal cycle: std_logic:='0'; |
|
begin |
|
wbm_ack_o<=ack; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if wbm_cyc_i='1' and wbm_stb_i='1' and wbm_cti_i="010" and wbm_bte_i="00" then |
cycle<='1'; |
elsif wbm_cyc_i='0' or (wbm_cyc_i='1' and wbm_stb_i='1' and (wbm_cti_i/="010" or wbm_bte_i/="00")) then |
cycle<='0'; |
end if; |
end if; |
end process; |
|
process is |
variable delay: integer; |
begin |
wait until rising_edge(clk_i) and wbm_cyc_i='1' and wbm_stb_i='1'; |
ack<='0'; |
|
-- Random delay before the first beat |
if cycle='0' then |
delay:=rand(0,3); |
if delay>0 then |
for i in 1 to delay loop |
wait until rising_edge(clk_i) and wbm_cyc_i='1' and wbm_stb_i='1'; |
end loop; |
end if; |
end if; |
|
if ack='0' then |
wbm_dat_o<=("00"&wbm_adr_i) xor xor_constant; |
ack<='1'; |
elsif wbm_cti_i="010" and wbm_bte_i="00" then |
wbm_dat_o<=("00"&std_logic_vector(unsigned(wbm_adr_i)+1)) xor xor_constant; |
ack<='1'; |
end if; |
end process; |
|
end architecture; |
/lxp32/tags/1.0/verify/icache/src/tb/tb.vhd
0,0 → 1,104
--------------------------------------------------------------------- |
-- LXP32 instruction cache verification environment (self-checking |
-- testbench) |
-- |
-- Part of the LXP32 instruction cache testbench |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Parameters: |
-- CACHE_BURST_SIZE: burst size for cache unit |
-- CACHE_PREFETCH_SIZE: prefetch distance for cache unit |
-- CPU_BLOCKS: number of data blocks to fetch |
-- VERBOSE: print more messages |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
entity tb is |
generic( |
CACHE_BURST_SIZE: integer:=16; |
CACHE_PREFETCH_SIZE: integer:=32; |
CPU_BLOCKS: integer:=100000; |
VERBOSE: boolean:=false |
); |
end entity; |
|
architecture testbench of tb is |
|
signal clk: std_logic:='0'; |
signal rst: std_logic:='0'; |
|
signal lli_re: std_logic; |
signal lli_adr: std_logic_vector(29 downto 0); |
signal lli_dat: std_logic_vector(31 downto 0); |
signal lli_busy: std_logic; |
|
signal wbm_cyc: std_logic; |
signal wbm_stb: std_logic; |
signal wbm_cti: std_logic_vector(2 downto 0); |
signal wbm_bte: std_logic_vector(1 downto 0); |
signal wbm_ack: std_logic; |
signal wbm_adr: std_logic_vector(29 downto 0); |
signal wbm_dat: std_logic_vector(31 downto 0); |
|
signal finish: std_logic:='0'; |
|
begin |
|
clk<=not clk and not finish after 5 ns; |
|
dut: entity work.lxp32_icache(rtl) |
generic map( |
BURST_SIZE=>CACHE_BURST_SIZE, |
PREFETCH_SIZE=>CACHE_PREFETCH_SIZE |
) |
port map( |
clk_i=>clk, |
rst_i=>rst, |
|
lli_re_i=>lli_re, |
lli_adr_i=>lli_adr, |
lli_dat_o=>lli_dat, |
lli_busy_o=>lli_busy, |
|
wbm_cyc_o=>wbm_cyc, |
wbm_stb_o=>wbm_stb, |
wbm_cti_o=>wbm_cti, |
wbm_bte_o=>wbm_bte, |
wbm_ack_i=>wbm_ack, |
wbm_adr_o=>wbm_adr, |
wbm_dat_i=>wbm_dat |
); |
|
ram_model_inst: entity work.ram_model(sim) |
port map( |
clk_i=>clk, |
|
wbm_cyc_i=>wbm_cyc, |
wbm_stb_i=>wbm_stb, |
wbm_cti_i=>wbm_cti, |
wbm_bte_i=>wbm_bte, |
wbm_ack_o=>wbm_ack, |
wbm_adr_i=>wbm_adr, |
wbm_dat_o=>wbm_dat |
); |
|
cpu_model_inst: entity work.cpu_model(sim) |
generic map( |
BLOCKS=>CPU_BLOCKS, |
VERBOSE=>VERBOSE |
) |
port map( |
clk_i=>clk, |
|
lli_re_o=>lli_re, |
lli_adr_o=>lli_adr, |
lli_dat_i=>lli_dat, |
lli_busy_i=>lli_busy, |
|
finish_o=>finish |
); |
|
end architecture; |
/lxp32/tags/1.0/verify/icache/src/tb/tb_pkg.vhd
0,0 → 1,17
--------------------------------------------------------------------- |
-- LXP32 instruction cache testbench package |
-- |
-- Part of the LXP32 instruction cache testbench |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Auxiliary package declaration for the LXP32 instruction cache |
-- testbench. |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
|
package tb_pkg is |
constant xor_constant: std_logic_vector(31 downto 0):=X"12345678"; |
end package; |
/lxp32/tags/1.0/verify/icache/src/tb/cpu_model.vhd
0,0 → 1,169
--------------------------------------------------------------------- |
-- CPU model |
-- |
-- Part of the LXP32 instruction cache testbench |
-- |
-- Copyright (c) 2016 by Alex I. Kuznetsov |
-- |
-- Requests data from cache |
--------------------------------------------------------------------- |
|
library ieee; |
use ieee.std_logic_1164.all; |
use ieee.numeric_std.all; |
use ieee.math_real.all; |
|
use work.common_pkg.all; |
use work.tb_pkg.all; |
|
entity cpu_model is |
generic( |
BLOCKS: integer; |
VERBOSE: boolean |
); |
port( |
clk_i: in std_logic; |
|
lli_re_o: out std_logic; |
lli_adr_o: out std_logic_vector(29 downto 0); |
lli_dat_i: in std_logic_vector(31 downto 0); |
lli_busy_i: in std_logic; |
|
finish_o: out std_logic |
); |
end entity; |
|
architecture sim of cpu_model is |
|
constant bursts: integer:=10000; |
|
signal re: std_logic:='0'; |
signal lli_adr: std_logic_vector(29 downto 0); |
|
signal request: std_logic:='0'; |
signal request_addr: std_logic_vector(29 downto 0); |
|
signal finish: std_logic:='0'; |
|
shared variable current_latency: integer:=1; |
shared variable max_latency: integer:=-1; |
shared variable total_latency: integer:=0; |
shared variable total_requests: integer:=0; |
shared variable spurious_misses: integer:=0; |
|
begin |
|
process is |
variable b: integer:=1; |
variable start: integer; |
variable size: integer; |
variable addr: integer:=0; |
variable delay: integer; |
begin |
while b<=BLOCKS loop |
if rand(1,10)=1 then -- insert large block occasionally |
size:=rand(1,400); |
else -- small block |
size:=rand(1,32); |
end if; |
|
if rand(0,1)=0 then -- long jump |
start:=rand(0,1024); |
addr:=start; |
if VERBOSE then |
report "Fetching block #"&integer'image(b)&" at address "&integer'image(addr)& |
" of size "&integer'image(size); |
end if; |
else -- short jump |
start:=addr+rand(0,20)-10; |
if start<0 then |
start:=0; |
end if; |
addr:=start; |
if VERBOSE then |
report "Fetching block #"&integer'image(b)&" at address "&integer'image(addr)& |
" of size "&integer'image(size)&" (short jump)"; |
end if; |
end if; |
|
while addr<start+size loop |
re<='1'; |
total_requests:=total_requests+1; |
lli_adr<=std_logic_vector(to_unsigned(addr,30)); |
wait until rising_edge(clk_i) and lli_busy_i='0'; |
re<='0'; |
addr:=addr+1; |
delay:=rand(0,4); |
if delay>0 then |
for i in 1 to delay loop |
wait until rising_edge(clk_i); |
end loop; |
end if; |
end loop; |
|
if (b mod 10000)=0 then |
report integer'image(b)&" BLOCKS PROCESSED"; |
end if; |
|
b:=b+1; |
end loop; |
|
report "Number of requests: "&integer'image(total_requests); |
report "Maximum latency: "&integer'image(max_latency); |
report "Average latency: "&real'image(real(total_latency)/real(total_requests)); |
report "Number of spurious misses: "&integer'image(spurious_misses); |
|
finish<='1'; |
wait; |
end process; |
|
lli_re_o<=re; |
lli_adr_o<=lli_adr; |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if lli_busy_i='0' then |
if request='1' then |
assert lli_dat_i=(("00"&request_addr) xor xor_constant) |
report "Data mismatch: expected 0x"& |
hex_string(("00"&request_addr) xor xor_constant)& |
", got 0x"&hex_string(lli_dat_i) |
severity failure; |
end if; |
|
request<=re; |
request_addr<=lli_adr; |
end if; |
end if; |
end process; |
|
finish_o<=finish; |
|
-- Measure latency |
|
process (clk_i) is |
begin |
if rising_edge(clk_i) then |
if lli_busy_i='0' then |
if request='1' then |
total_latency:=total_latency+current_latency; |
if current_latency>max_latency then |
max_latency:=current_latency; |
end if; |
end if; |
current_latency:=1; |
else |
if lli_dat_i=(("00"&request_addr) xor xor_constant) and current_latency=1 then |
spurious_misses:=spurious_misses+1; |
end if; |
current_latency:=current_latency+1; |
end if; |
end if; |
end process; |
|
assert not rising_edge(clk_i) or lli_busy_i='0' or request='1' |
report "LLI busy signal asserted without a request" |
severity failure; |
|
end architecture; |
/lxp32/tags/1.0/verify/icache/src/make/sources.make
0,0 → 1,18
# CPU RTL |
|
LXP32_DIR=../../../../rtl |
LXP32_RTL=$(LXP32_DIR)/lxp32_ram256x32.vhd\ |
$(LXP32_DIR)/lxp32_icache.vhd |
|
# Testbench sources |
|
COMMON_PKG_DIR=../../../common_pkg |
TB_DIR=../../src/tb |
TB_SRC=$(COMMON_PKG_DIR)/common_pkg.vhd\ |
$(COMMON_PKG_DIR)/common_pkg_body.vhd\ |
$(TB_DIR)/tb_pkg.vhd\ |
$(TB_DIR)/cpu_model.vhd\ |
$(TB_DIR)/ram_model.vhd\ |
$(TB_DIR)/tb.vhd |
|
TB_MOD=tb |
/lxp32/tags/1.0/doc/lxp32-trm.pdf
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
lxp32/tags/1.0/doc/lxp32-trm.pdf
Property changes :
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: lxp32/tags/1.0/doc/src/logo/lxp32-logo.svg
===================================================================
--- lxp32/tags/1.0/doc/src/logo/lxp32-logo.svg (nonexistent)
+++ lxp32/tags/1.0/doc/src/logo/lxp32-logo.svg (revision 5)
@@ -0,0 +1,104 @@
+
+
+
+
Index: lxp32/tags/1.0/doc/src/trm/frontmatter.tex
===================================================================
--- lxp32/tags/1.0/doc/src/trm/frontmatter.tex (nonexistent)
+++ lxp32/tags/1.0/doc/src/trm/frontmatter.tex (revision 5)
@@ -0,0 +1,70 @@
+\frontmatter
+
+% Title page
+
+\thispagestyle{empty}
+\pdfbookmark{Title}{bmk:title}
+\calccentering{\unitlength}
+\begin{adjustwidth*}{\unitlength}{-\unitlength}
+ \vspace*{\fill}
+ \begin{center}
+ \DoubleSpacing
+ \includegraphics[scale=0.2]{images/lxp32-logo.pdf}\par
+ \vspace{\onelineskip}
+ \huge \lxp{}\par
+ \Large a lightweight open source 32-bit CPU core\par
+ \LARGE \textbf{Technical Reference Manual}\par
+ \vspace{1.2\onelineskip}
+ \large Version 1.0\par
+ \vspace*{4\onelineskip}
+ \end{center}
+ \vspace*{\fill}
+\end{adjustwidth*}
+
+\clearpage
+
+% Copyright page
+
+\thispagestyle{empty}
+
+{
+\small
+\setlength{\parindent}{0pt}
+\nonzeroparskip
+
+\vspace*{\fill}
+
+Copyright \textcopyright{} 2016 by Alex I. Kuznetsov.
+
+The entire \lxp{} IP core package, including the synthesizable RTL description, verification environment, documentation and software tools, is distributed under the terms of the MIT license reproduced below:
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ``Software''), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+\vspace{4\baselineskip}
+
+Altera and Cyclone are trademarks of Altera Corporation and registered in the U.S. Patent and Trademark Office and in other countries.
+
+Mentor Graphics and ModelSim are trademarks of Mentor Graphics Corporation.
+
+Microsemi and IGLOO are trademarks of Microsemi Corporation.
+
+Microsoft, Windows and Visual Studio are either registered trademarks or trademarks of Microsoft Corporation in the United States and/or other countries.
+
+Verilog is a registered trademark of Cadence Design Systems, Inc.
+
+Xilinx, Artix and Vivado are trademarks of Xilinx in the United States and other countries.
+
+All other trademarks are the property of their respective owners.
+}
+
+\cleardoublepage
+
+% Table of contents
+
+\pdfbookmark{\contentsname}{bmk:contents}
+
+\tableofcontents*
Index: lxp32/tags/1.0/doc/src/trm/images/ibustiming.pdf
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: lxp32/tags/1.0/doc/src/trm/images/ibustiming.pdf
===================================================================
--- lxp32/tags/1.0/doc/src/trm/images/ibustiming.pdf (nonexistent)
+++ lxp32/tags/1.0/doc/src/trm/images/ibustiming.pdf (revision 5)
lxp32/tags/1.0/doc/src/trm/images/ibustiming.pdf
Property changes :
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: lxp32/tags/1.0/doc/src/trm/images/llitiming.svg
===================================================================
--- lxp32/tags/1.0/doc/src/trm/images/llitiming.svg (nonexistent)
+++ lxp32/tags/1.0/doc/src/trm/images/llitiming.svg (revision 5)
@@ -0,0 +1,334 @@
+
+
+
+
Index: lxp32/tags/1.0/doc/src/trm/images/symbols.svg
===================================================================
--- lxp32/tags/1.0/doc/src/trm/images/symbols.svg (nonexistent)
+++ lxp32/tags/1.0/doc/src/trm/images/symbols.svg (revision 5)
@@ -0,0 +1,662 @@
+
+
+
+
Index: lxp32/tags/1.0/doc/src/trm/images/blockdiagram.pdf
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Index: lxp32/tags/1.0/doc/src/trm/images/blockdiagram.pdf
===================================================================
--- lxp32/tags/1.0/doc/src/trm/images/blockdiagram.pdf (nonexistent)
+++ lxp32/tags/1.0/doc/src/trm/images/blockdiagram.pdf (revision 5)
lxp32/tags/1.0/doc/src/trm/images/blockdiagram.pdf
Property changes :
Added: svn:mime-type
## -0,0 +1 ##
+application/octet-stream
\ No newline at end of property
Index: lxp32/tags/1.0/doc/src/trm/images/resetsync.pdf
===================================================================
--- lxp32/tags/1.0/doc/src/trm/images/resetsync.pdf (nonexistent)
+++ lxp32/tags/1.0/doc/src/trm/images/resetsync.pdf (revision 5)
@@ -0,0 +1,220 @@
+%PDF-1.5
+%µí®û
+3 0 obj
+<< /Length 4 0 R
+ /Filter /FlateDecode
+>>
+stream
+xœ•½Í®&½Î6¯«Ø7íÒ¿tx`gxì¤ý"èØøöµIiw7¾“/ÎÙM–•$RüÅúïÏûÿþËüøÿ×ûñßþ¿çýœÿscÿ÷ý¿ÿ÷ù?ÿëÇûù~üßOýøOÿý#±ñÿ†?)÷Ï··žçÇׯ'Íú™[ýhåsÌôñë#ïÇcGüüø?>þó“w¯ÿF'c¿»²>û»>jþlù£õϾÒÇÿøØÁûowRù,sœQ¤š>Ä¿ñ{{YJïg¿Ïã³µ˜Ÿ™ù3å„^°†‰ÿþÇûhó3í©¤öÙæÚý9\?ç;?Ú`§J£ùL
+úúhí3ÀPV[þÒ õûpÿ,£ú/ÇçûŽèVÐóoµÇ6"ûå·ñ~}üóü>ƒOZŸimÌü\cÖç[àYÿTüÿ¨
+[Ž·ïÞö?
+ŸõÖ>6Ë|¾cÜ«¿‡3ßåÐà:Êõ4í—šœælì-Ÿ
+¶Î¦+ÎíQÍH@IAûœ#n›Y7ÔŸ²»=ÅQŸü~æœÕ¸Ö¸M>kåôù¦uA--µ%œ?ß2ùËV€•Ð^…ýïY‡šŽ½¹ð5ZûφZæàS#4ZA[e=@¬ ¨×ô‘7KTBý`ãXÞ]l¸ô¨í)o¨ææ«ûHR€]ýñ&ÌbÇõÍF5öSÔt¬AXô]SÛ¯¡73dþÒÖl?î
+ãíµbw€ö…ê¤n)œYe`Ý9ƒ^Æõ¸¦ÉŸNqÀ¦ ñÒžýäÓžÉobOujÔaíüebKŽ`cv@X±-hÖPËTHÄònFïl¯Ú`ôv0•¿{÷BeïtsÍë$̇0šM®½nFÐLv{-Ùv€ÕÄ)r‰k#As¶YN—½>•?ÎXÚ¡m¾ô–A2D²ä·ÄÓ¹Wö!T“oÒEzŠÜàŸýšÒ´©&¡5c«ïLñð»0øñìe¿é¦L lø§Ãà¦Íž„ÿµ_ÀêmÙã~ï~¿oï¼½{7)ÐäºÚ#5ð§çõÔøÖûŸhÆã½ë6Ø Á6Ý hrZïe_ýîk{lz—~?ņ¶žOvXÔáïœÑ
+lÈç þùcº?ŽÿךՔÔ/W
+¡´~S|¦Åh ü»Ô%”Uþ÷v3
+ÅðظÊî¥oÅ°Wc[àáß4ê¨T¿£m¸Ûn°o غyWЖòš[&ô…¼Éžn™Ê.ÿ¨4¨AllWßVÕæe}Í ‡mç&Æy¼¶p¨sËô"ê<ƒýo9²‡$!·¾¤Ì†[ÑDö–â„:f75HpÎŽ¬3@½·‡$ú^Ú¹Õú†êæË
+QSìø 3ŸZÂ-ßzAeà¹àwÿåæß-÷¼ÛôI•
+sz÷üÙm®Èü`¸y ,ͦÆ5ÛÖåu˜è^¥¾5ñ̾ijÍ0ÿýp¡Ú¹àƒD‹±®§›¨ éµ h“OtkŽÆZ¤;66¡e¬³…¢Á ÏL*çåO[‚nv²§¢OÓkH‰ð‡8·g2t£1xÈ:8F*€ƒÍaòsj+f´n_›@{«Ã`îb'[´]ÚÞZ˜P†ÀZè50ƒle
+Û'„ADz7+àjt-T^$û«óMòC…Œ1K´Ð 8½x:ôVþ{bu6гz]owP= çÕ¶SœzÛ-Ð:Ÿ&tØ©û6”«±lì·LmÖm›„- ÞÔ˜Bâ¥ßÐÀRîRÁà0&6C³1ß´Õh¿¡ýBl9¬Y@Åa`ßzИ¡_žè¸¸Ä3‰Q´Ìƒ¶Ä-Æ-?Âúä½ÙQNä ØXˆIIó6Ù(S¬ÌÝX‚)¿só2øv ¶zÈ{µ·œÇ0Q_À€¢‚d´uÉ»(Å»ü/"áwî)„€¯æ]¤ƒjöç“û˺8j·¡