OpenCores
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&&reg>=0&&reg<=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&current_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"&reg2(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)&dividend(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"&reg2(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 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + ADDR0 + + + + + + + ADDR1 + + ADDR2 + + DATA0 + + DATA2 + clk_i + lli_re_o + lli_adr_o + lli_dat_i + lli_busy_i + + + Slave wait state + + DATA1 + + 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 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + clk_i + rst_i + lli_dat_i[31:0] + lli_busy_i + dbus_ack_i + dbus_dat_i[31:0] + irq_i[7:0] + lli_re_o + lli_adr_o[29:0] + dbus_cyc_o + dbus_stb_o + dbus_we_o + dbus_adr_o[31:2] + dbus_sel_o[3:0] + dbus_dat_o[31:0] + lxp32u_top + + + + + + + + + + + + + + + + + + + + clk_i + rst_i + ibus_dat_i[31:0] + ibus_ack_i + dbus_ack_i + dbus_dat_i[31:0] + irq_i[7:0] + ibus_cyc_o + ibus_adr_o[29:0] + dbus_cyc_o + dbus_stb_o + dbus_we_o + dbus_adr_o[31:2] + dbus_sel_o[3:0] + dbus_dat_o[31:0] + lxp32c_top + + ibus_stb_o + + ibus_cti_o[2:0] + + ibus_bte_o[1:0] + + 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·¡Y0@Àžw§-Õ=”wºŸp79nîÇAöU¨ÀðÕ¶– |¬ýåÌœo»ÁåÍÙzÈ\ôÇ[LW•ŒÕI7!ü7 ŠL'80M/Pëlss.Õé÷WVØ.Ó½8€ý„`®&Z‹­Åú<~œ 'ìÆó~\üTŽ\ôlž_<Ü“”RyÜ—ã¸W;¾ç%cÎ@ÙT'¶(áÍqÕÊ#wNKšÝ_ãúPô¡vž»·éùJ WE¯6¦ôpòÈy¸[Gî™ùøu`/så€xÈnor׎Ü9úqíȼ-|=ò¶LŠXcuzwv*8(÷cþr†2hÒa~s}!·ÜÇã„szî­,÷òÈßm?ó7ÁÆN;È<=.G™!ÆÔ>Ét \« +X™³çëÞ^ äî=?õ‹TC°Wwøì…ÇãӀ¯#8wG\Í[‹öœïyÞ“=´Òñæ¸|Cœ + û¸†ë¼Û+fÏoiîÿ‘Qg{ŽH^–Ê-ÆÚæìò8»½Šé¼Gûß½@@#s­9¿ær›Áζ”–õXsCÀŒ_Ó<½—¤&|ä rl^Èäž• +š>SÊÑ[úÝB¬Ì¬—KDM?Ú¦6¶3¦½6Á_÷Åv gb4ò¸…ñ¾ÕÝÂù*²#·pCCn¹…ÑËåN˜=áöm¨ÈÓ2¾š/ÝŸoeÆ“¹…¤/tÜÂ! éná€HáŽéc3‰ˆ°Y9Žá D|äL+]~!üøQÂ/„§.™ËW]¾ …ÁÐÿs7«Ä=çý—_èˆp Ø¢gˆŸß®¡bÇ3<Ã1dࠅ߇@‡,pÓ‘ÃŽu¢Át™«Î§<³p S<~á˜T©ò ò¸éø…C'îŽ%?S~!è6úånÄJ|LË +T‡sâ~߆±Ž_.Ú›äzËÆýÂár ŸÍÈ_ +«Þò Gça{†£K„gˆØÊåb©WÇAÝy9†à«œî竧p ‡bÇ1Ü8ùîŽ%aŽ!g”Ë1äò„_ˆ=³)õ¸_h{(Ì,ÞœábÕæ~áÌ/âø…è­äð 1–Ü/¿pX ÇÃ1#ÎÛ²/Ìí‚íêy®utÅ|v€ÙçCâÇÃAErÃÑ. +ßqBÅ&iGŒî¡@ý˜:1CÅpcèažáwŽ¡4dàñæ!‡éSsæ"þÖêå">žºù…&—Â/Üs¥ÔÌ/D³‡[(Ùá^!Ø6ïÇÅw“ɦ•qþ ¯Û7_ÏåH•)* +iæŠmX÷ +!†5z…¯ð`è:C›WP:ϼ±Bç-‰uóåBxÔty…CncøRsô +Áƒrú|á)¬ŽWQöÎðú|uÂ+Äb–ë9å®»… Sï·[BËÔS˜WøeþÆD?žœ3#°Õ&ÏF7bl‡;7 +‚á™Ì)#Ö ¨ƒQïeá6Çöq2 x5‚ ™h^hLÅóÂx»›¿×«kO·ñ¥fÍ©2ô³ÁÌ#R˜|["m€¨ìîålåÔ0Ę„·‡ +§´ÏbÍ1Õ!Gà„™Š3ïv@H•jÍ'9Uq ö·xÊÉ—7ÁÍní÷ò1ÌŸ +ç²A’jUaôÒ|mx(¸i¼õs#ÚÞvò O†1¡:n_—·x~Š6iéÐáã‚OsÑ:ÿßõ:¬=•³ºgžD¦x!˜k_àhc¬ÇŸCQÅìV,wLéOšÿ¬õ¹1…2Þ¤ÁËW…F“V†ã\a¬~â ŠL2ƒÉÅÙ‰ePY_qWWïÆ]ÌúÝ…9–-°b¹ËYêß…m‹*¦`l ] °Æ‘Z‡ý>AÆ¢:'þEDCþô²‰˜w%æãΚ:øÞjÀ¶Ê\ÛÜ†ì… +RŠ2ù€ ÷?Uj³öÙŽ¼æ"ø–¥ã%´ïìàÌ¿ˆèUY[<€á4šõ€¸2÷{sÒÑ)Ñ5Z${Àˆöc­ +Óª^ž§gj«Þ2¤óhBèÕÈKél“ª©N¤EÔJšHÓóiÉ0Ó-/ÛÞz%ºWø®‚U/ÏcX{då€k–XxÉËZTôd„Ź‰è<ÿT²šÆVËÙp“J}Cuj"¶ß6øˆfí{’PnI8¦D Ù|-+øÒûØaÐ!Öå)5‰å5ÈÏIYÙ˜' Z fs§õ¼Ç©Ügµñ^’aÓtÀDV 9Ów©yà{úY‹Ò%FÅ™Sƒ„3`’ðÒÀ4ëSê2ÓŽŠ mµ¯•gž³‰¾\Ón: À’Ëcí +óΤŸwÆ2µè»{%ôü0u:? !’‚àbI½¢ ÖÔ$,}“´ë%œ' +ª XeÏ‹¦Å°5_´cuáPR€@ÛßézÞ=L©Ü¥Žækš/ÛÂÔeË€YM«kϾJHx«ó +OÁqøҥܺÔ20‹¶mø +¤nBÒ‚Ë+åÙP×îÁZjâ"d 1 +v’STÊ2Ò¯›Öêµæ¦i¦l_&‰‡Pص +-]䈦Ä4ºâ$…ò{FËñØ ÅZ6—dÇÝo’_ºá7e°õ@¥kA_äZom[ñó Â:@›Î7¾!žû7:ýiV +zo þùs(?žÙèTÎÎüõ1u*p@´À"„}³!…pf§I8o×n +C€ s_‰gÓž¡;…›ñ +œg#ì§HÜ“ñ”Ç0'ycC3¡ñ”?½aäÏEwCÌó™ò®7È ùq +»A¼h..g¢j>"û~Pœrƒ]À¿·¨‚Náp§D*½ãÄñ,B˜§øä£r°mv‘p;x˜gãcøyS©ô¯’VlÙÇ{±mýè +@™qÎe£ÚÁË4ˆ'.W[ÙEöðÍ#zš]Þb´U¾à=‘©X? íåL„n`Ó-ø +8‘WÎèíô|™é0 +•Í—Æ‚ÿó€}ö«@I&*b @ŽQ+¥¿iÌ[ÇA˜[lðÃw-µmþbåŒc*†ñÓ +â­?|\y¦œxõšÔž¹€ç ­JÈh‰¸¨à'†¯Ç˜Z*ßC§§fÐ8ÔNGl\ï§ým1$Ã99úÖS!KÀiku²-iO+:<'áèjê)“¦T4¹««-þ†Añý™4 €lWܵ;¶ÒÚÀÌì”HTA_ϵíáC“hgˆªr¾ÃB<8\þ‰%¦’VpÖ”2C_ÙÅ¡~±ú][{¯ ÆD‚t|.ÐfðÜ‹‚ŸJׂõѱ²×³¸pÓyqiè¨n2.Võ"À×_H²7a#ÐH!‘¦ä¢àŸg +° +Ë‚ÿ†Ùi;í¼ûw›ìtðÈÃfh\—ÞŽ…Ä°pîjùªšõÄEWÄÎŽõ€Èé~Îœ¥ LÞü1š^Ö@7ÆÌõî^‡f¬ìÌZ’…F¸\Mïçf³„h͆ ¶ú±ÙžÀ8 »^,Í®k§rR=¶6gd¶š „ËC(rˆ[ý?†áL¶‘‹º€Ýzó˜ýf¿~Übï9 ŽˆYïçÈ\1Žëû†•f €1ÃÄd.pžæœú±ÍzoOð·æH¼¿šWYk›nu„12L&q52ƒ°WËeË‘‹-êµ´yÍ•ajX~š5ç{\A{îlîd£ +ÐvãAôj¿•À%(ô +™Qw ˆ´åZ·Ú0ð>õÜÙÇì:Ny43ì´$ÆkÊÆØ\¾1- +°Ü¬7•Äwé•?Øu‹àžd ¹ fm&)|eÛÃ=óä‘&L#@‚vCãØxW_ñ°h0&…7X¼j·ó€1{ñ¥-Œ3˜¡· +`Š°ô€ !Sïl¦Ÿ.®êîèYîàL³\_dî]CI_7†œîÊt“/—ód×b E™³+¦ÒDžݘ™¯nø±Á¬ÇðK½IX†ðÙoàO,ØýS«[û1þHÞÌú»W'¢ö-¬—#ÂzÖ9˜ñ(j—ƒy®‚W‰¯ÅAF#jflÍ%Þm‡"nn¦ŸÚÓ0‚ ˆ.fì"ò¬ÍÁdžìCò`sQ„ƒ©þŠ‹XKÜðÿc6!™~u’È= ¯F `òçÒõºÃ^ÀCÏ1 )òì™wÓå+œ¥¼Hóõ7jawrJØU©ëÊs‡pk?åwø9Ö`êJfþBö wñ,sðñ—¶| Ä@fìßYüŸ?çòïº<Eêb\ Š*Üe^üË" ªë`­ã*êÿï_«¬ÄÈ´v÷ϳ<.ÁÿòçvC57Š—_tŒ2üÓaKþ—Ý}ÿ¹v7vÑŸ#¾u¹Ñðk™n.íùËk±V|  ËkÇ« ’Ë·åµàž'â&þKAÞñ×A(ûÆ~œÞÌ< +ëÛÀÇܯtžû8í×߇MŸê÷™ Ÿî,¯dd. +Á?yƒVñ[IП,¡À›CJîåuâ.BqVÎÐõ‹EW÷ ÀótùuÕŠ$š Å:B½]A€áJüVÝ\\s·K³ý½[„åmüÂé×G öLUf ]YÁYV¶¡v&~Њև¥#^¿Ñ¢èÓúª:¦øz +틲£Ú}1^/· !½Øiû¶‰ì¶ S3è´ëNWÃ{¿@2Ì0\Â5”>Ø vèÃ!m²´)© ê—jT݂Ȭƒ :'=àhƒ‡ãŽÑ©ÞK«œ72pWþU +#ÿ¾ºbwçË«çæ8îÒ¯ä°Ú¯pV0€`aŽªÆÈûâªwëmàÔ'+ø¥Ul¾ì`OF%{üN’ЈÒT&»_⥄D~Ð<ùí˜äTžvß„Cív¡ëeëg‚s0Cpª¥«’¨\ù²¡Ã¥è\§I Þ`áu s¤¿n Nâøs]fQ +Œqèà¡'›‹‡Þˆ&dƒkÓîV£RÁÈ&ƒµ2æÔÍ™”H⩪•Q} pÝZÚ=âJ¤™Zƒ<šn]?2{Ót’å=†Ý³«F¶eû…g©åÓî4^@c–ã×íÓ5ym¨àˆÆ}ù¥wé®R2³"ºÆæÈoãm°Í†_d VÚ2Š÷™±q€„XûÒ:™T$ÉYªæÅ +’%æÏËa£Q®…{_¿ÌÚ¾:W´™/Ç‘56fO‚YøFÌ»G:ú½Ï¥«uèõ¸67µuò†4‹]»+ÙøÓ®Ýå®äˆFŽ j‡XHNÞ‘.[yÙXïæKe[DˆÚðµsÙoÂYw3þØ,&’†]Ñ(”rKoæc¦Ï߈žŠÝêjªé±ò˜À**ã!Uˆf„à­&« ÂkpÆ͸†…ÀZÜ"™ +i ¸·ÄŒé-ð²sc¬‘·£3´A±T‘À­ÆÑÅÅN•š} ­ð +ò~@”Ω*Ì$ðQû|0,O” lK¥‹ôsúø×ãAƒ/W%'SX{E[ hkW#«@v íÏ;÷kὧ +Ž2_ Ífˆ§“ +"i8÷¢°¶°¶TÅfg¤R¡£w\ÄûçO.ùñ”×F©Ž~}Bòíç@"AyCºBZáI\^ÆìÔ$Ç¥ò + +j4[6${®<Þ¢„y‘uR¦{h>Í`®3d9KS)ÔAEàÍ7âù8†UVŽØ_Áã3F-ïL<†1Ë ·(ˆðiÁ}ÌÓÄËT¢Ù´]3iÅpÒ̘ÕåO.’uGÍ¿lc†a°ÂТ¼faÉ2(¯"ŽmŠµ° +ºnÇ~(nM +C6Ç$IÝx© ¼1ú—‹º+°;a2š.0-fºæض0ÝæÃjslLß|XC”€Ò¬ÆþP7[÷bÿ +K+C_ì)”OÛP&jø„ýÚËN«Ü—ÅUreôî ñœ$«ØFºEßó¸„ÿ†q L9»y d¡MAö’L¡7}û"Â.AïÝ^¾™ËdÓtLÊíÁ¬œr¬6ê­ +ãÒ)¿&ÚŽÅYÊÍgÖeŽ«Aý´Rs»H¾ä“âŠK^Í‚ +ƒ©²¥ÆÎíL +sY&(;ÐÛpÿ Ô°Eïc>Fáä{Ž#m9ĦrIÎÜí_>×Y1yvåçÀ±°L.€îNÒØ]@9XØmÐææ(Ù¼T¥K•lŠ¨Ù†´12ø@ˆ$‚ù±ÁÔŸË"ØѱÙ\‘ì,V¤m.Ž+aÙñ8Ùd¬×JfË`™m(œZµKö6”†QËvô­¹wë4MZá5&™ŸfRƒgÊ Ò_QwÒˆv_«¸ VÚ:À£eÔÜ9—7Ë`öÓ#"v‘%µè¾3[•aª×¸ƒ~òÅ=Oß´R‘?)ÖœEi.@éæï :Õ¥šì¸ÖR”eÈÉÓ +„KÄÒŒ}~G¤—ñÓ~òüñ½.x¬op3-0Zéámv0[é&I}¢z‡¶+×eÜ©Œß4R·FzH[%éšà¥‘æw4¿k¤yk$c‡K%¼5Òü]#é¨¤É +ñ„Nš¶AÜïÄ¥’~çèOuQ´ 6bºPÚ®OMæÊÉGÞ I$mËš˜"lÃÉ䌯úFïb¢@¢›L>—üŒu4Ißx*ŸC}¿™„bDûfÎW\|>ŽSßà’@øÊ#WƒN&Ô_NÙ·a—²Ç$—½b[t¦D„Kä!ŽfkäJžáp+² ÊCJÇ6ƒ0úƒ(?—9„ù¯@PKGGÖ> +åa-L¥7ù6{¬^kXzâüè¥ÜϧÍÛ¦¶3`ʵÚâ÷ˆÃà¾)1”À-ý«ÍÖÓYE.šZ¸\›Í|˜B­*£ba +»àN ¼9@×p‚ÎX@@‚~?·jªcã6w~{LSS¬Šyó\–·ž‹hm´Ó®UAZâ÷°¨j±ðOS¦EuƒÌ5\Í=MUuØa6@UȤIÿnT] ²ö»©Š¤‚ƒ‚«cö¢¬ŒÞŠ`Iœ¡Æ/2?7è"„aµÚ®a ÔñµXµ#iàZBÛYeµX°ÖÀ^i·/)uœ±Vî$7”U´Â +Q9lq{g²šX”Q-ü)&d ¡VÙî‰PFA ¶£µ›Ej‰%µ2#Ï•v­¡CR0Q·Í›åZòñ¯­(-]ýŠØ˜ÑŒRêoDý+·ðXŸýhi +²c çµHBѪ#ÌÎ`…žƒó[•ƒ3˜¡?l3²=ç½e×su†4¡š«ƒ Iîü¢–Á{œß:Ž†ðæ#s~ëø]EŒßUÄø]EŒKD=†ø®"FÌ +¾/içàû¢ƒE!Œûç9”€ïË,GžÎï&oW˜|¾,ô&Côhß|ߺxí×-ºÂO€¥±Áe~º|ߦzfg56$}XI$ÂËúŸ6Wî’aÛB·‘¦h|'>›Áˆ(iÆÏK5'k¸Ç«•ý:W¤ÈÚ+ùÄãÛþn¢<‚&߶3Üfœ½Ø„gXÅLŽjÓ&Té@¶)v©g¢^=ð(SaBAcæù†ð­ìK,Ñ°z‘IKj"Q2q0µ ýoº‚\ï:¶õ˜&p»ÿÜ{ë›…Pª½o8·ªÞø‘Î2F:ð#èÃ+½Þ,"ÐèZ´f6 +Y±±VŸ–©æÎ,x_óc¬ÎÚßX$Z„y" +ÅþÇ^üñôoÕ9] ”XÑíØœyð—(fÕSø)`“žLµ*¯¢§³Ó­µ4³Â>=Y„Q‰Ýcø‘Y±1ÖB½ŸR›6 ·Üâ΄H.·< ˜ºn•6ŠÙ¼æS{€¹îCbòÍ—ЃqhÔBñ»Êî,qç|x¹—”XÑßM΃{XæžTÝô˜F"ãÚèÀŒjðr-`Ó•~B’s=™ÃÀvwÓ@JbƒÞ¹6}ÉböB#d©ØOG© ¬õŽèS÷R+.ÐR+. R+øsóø2—º›‡§Ô +¬½j—Y¦„Æ–\ýÆLS¯¡ÁÂæk&REòÜó*@&Ëœ`‰©î§êÊ«èÇŒ¡ôéº)æ‰ÝÓg”XÑóµ]5·Ô!è^&«ÚV¼ Ϭ¸Ì=‡SM3"XÂS+,»ÕS+8ý©Ý£Z.¬,{úñStÌ´ÉMÃYWH—äXªÉ`‘UBöäg‹.hA£~Ž¿º'Cè×ï·¸'[¿&ˆ‚{¸7ÍŽ^Ø©â^O­ˆî©ø½Ù¾° ¸y5w¦V`ïæÄ’CnJò\©Î@í0P®aWK&³(æ˜~ŒÂ$u)ÚðUbÅwᬓŽ?ö–áã¤|1ÞØÇ9 +/Œ“U™ +ê¢Ë†,k퉭±ýÙ{ônLß}O@…÷~ÎÚ±8,0¢òì”›B\*½7;·úáV2ià +y„H~¶A¾÷8ûD6‘ƒpï–³!qܻʣ¿w÷èî*ê!Þ~"¦Z†ÍQ.¸ù£Ðc#"ÆnÿA‘Ïp)Ù(‚¡—/U;«î +êÃÆÑ’øÃÏ˱#棚a׆ñã÷ØPËSñ<Ûþ*Íæù¡:?îö‘¯ˆš|À{U+±~s€º>6f®bׇuÀ´UõÿœA%i¦YÍg¨}ÚæëÚ|´ÍíˆA1ØÏ©›Šú‘ji6n,°b}¦D×÷ ®âûžã +çgZåQg*2Ö5%ÞYôm$3pVÍÜmµ'«ƒ¢Ö;˜%ã²EA^~€ÂÕ¤ÌïÁ›¤Ú*¨í¸zÆà7P‹ ‘T ÝS^!Oo]§e(¤è>8ܬ ®ªÂ8šåÈvR|ZÀìùÅ…Ÿæ3/ûx@DiÉP…±l²r]ú²(fS¾F_Ÿ&nËLéÆô¦î8b°ñ~—© “…Ÿ)SA(w³F9dòÞYŽo†²oxQUEÜ×qMªV +óKû'à¾Ü ÆÙ*æn\gq†±8—vÀ0“«+?õçœ,ƒ2ÜU<°×«·ñ†¯×÷¤öè*‚µ±Îo1è¿Ìã +^j$²ûQ(϶eRiwKñ”áϪÙö•ŒÌ¯83}üÈÓöªµ®]¤M%‹YI0üG‰äl3üG6R–?ªn›ÕÔ ¹"é<²lËðnù˜á?rdÖÊð94 ÿ +¦u"¤(Ãßnøº†ÿÈÎ'²ûQ¡³¸2àÜÞr"™£\ånKã±`kàÁß©JÔ®øL +ÔOûí~ÔF´³º®*BýN¨ÕÌ(7ûñs;ë´*BÙø:Yit?+µWÌZ¦Ýoßs6B­Ò÷„ŽØ:`DrEzO¨iœØ’ì~ÐÕz‡ÝÐ + }õÂA³û/ ìþ‘#Ѩ‰Ž~ö(»kßýë +*äþvÿðô•q¦*ßU†?Yج“¾p厰9"C‘Q +JyZ¿j|û)­s,¼éÀ¡ÊÌþ +}<¤§reT£Ôk‡yú Ù°ÎnÈ-ÿ Ë ŽTs¦pÃx” ÿq’kø]¦ê‚ZvÿàÕ=™êT3ö}L³ûC0¸iŽ…2K°øg;'®XsÏt”•Ý +<'I?Αg}{Öº™ýÜ™fÅè“NnOÑìW ˜fÿ8>,‘cæúæTþ CYd1Û—Ò¥£®ü'g ÝÒq%U>MËäyä¥ÛýØj–ì—4î"Â%œ¥¡þØ?ðÙ·n¡/3)g$ .Vrs·‰&åÛøR¡êöÜÏÅn•Ÿ—ž'uoɼZq•TinFŠP3PVˆ™)‡!Ä —OÅÓ‰ß9ˆå§ÝŽyy!y#d»~*cž«9,Ö,žZL2Oä5-Y㬚F¯Ì75‚ͳZJ§l¡YC<@«LœåÛa˜Ô +0æyÀø›aÕ¡¸åT*Î@ˆ¨d¿ô¯ë[ÛµN5×»e ÇÈPy$Ÿ‘›¥ÿœ©MÖ žaµÈzX2¦êÍpet3N‘Ói©uéUZÈÔ‡¡ô­a®¤is5ø`ù=@,ªwü€·'ËÅ5³¤j¤ÚwÍBü‹·\ñz¿ ÔùkwKªÑ-ûy*´pØ&K4‹¤Xˆqžœ.Z¶ØJ$2ˆàÒ¼.Ír:vž-‚¹M=GÌÜ@³?×è6͈Mìª=ƒ¬,‹Ý?T«6aQ¤;»VŒðEËcÿü¹#~<³ŸÜ¦ÑUñ½Î¢X‡ðX3s¸–1!1,×aêLΓdm…/=;¶X½ÁnÁd{>íÚ,"©sÛxYK²Xï–¯o,åé_°ÃŽ]‘¤´ó¹ìò +šG›84W„ZßbuB=]¥éVDNP\÷åœ_fŠ.ŠSÀe˜+Â_ÂÎkXébÅ‹WuHÃ(}«ÍÕ1,î¸â´ŸÁ»í±¾EYöjG,/áȸŸ_7ð¥BWœc0Ÿ>VŸðó“%N]‘VîÐXÅǸ(ùzÉÇá‡>,;Ù§Yð*éî˜lðy¼ +ºä”ÒtÎß.Û¢ò#>éMLa9GåVÐÙ³ +˜}y}ÌóTF°ò­æðX§û2(ÄykF–J +ÍieG9±× ‚f›ùR½P×Ûlí¹ºòÕ¬–.¥@b±Q?ÃAq± œ&ìzœŠ¿*Ô*í’ÅžrÒçO¹«Ü‘+9j¿6%5ì’ª#`?Û):Rï戔|›*?³4 +ÊM€.–û’Æ~éèµòœ®vôt˜ 5ÈNv¼í¬×ÃeñãåתȾ"ÙAk"dc©Æ\~«´—éeƒ=Á)¸v^\[ì‚€ +ùzzÓ SzÐ=º©çqc ÅwP»Ûúj¶zqt]ù<²QNl¡§…Á§fF{õØb—ÕTKÙ£mïU=–ÒlX­_‰61ð‡eí¢®$Ë›º½•iÙ†rÄŒSxvsFÞT±âËžU¨Cgà« +;ûèðJ±Õò¿ {sψ‚mÚÁGóÜúyt•V¶·QI¬H}1Ñáˆn¾¹çq„s–æ’6çPó1©’§ÅélóL÷!º[:3òD¬¬ZåN¿¤ÕÍi›Ã¬T~<±:¿Ï0GleůÝ'±£$Ž.£n +º_$Ø}öÆîÂA®ý€~IàB ±vþ#sDBÑŽ\b0FS¶çˆ+äïüóW–úÁZ3&Ít‘Îʶzgƒ.ÖAÙeõ\ȯ;‚Çj¡íø+ßW2Ôú:Ã\)Îí1ÛåΑG”Ö)¿À‘$ùÒžë·sø削(-¯|bw\×Á>­5ŠûµóDÈ ÝoȦù†ÙÁ;®èíº¿³¼2„å?¯S[!ž•NHHõWãر{ !YÖsJéKîñ$” +vG»“@q +2v(½šW‰$0 +¬i€`JôßÂOóZEqÇU€í£ÂYeh¯T •»¥¸Òç}éÕŠçR²C) {ØžëdÁ€^)6”äíRÞyÜqEé^ÏŠ)ä(?ìÞÞ#¦H Çõ“¼’IWüàOÏ‘æ\¥9Åb+ÿv(¾ÎÞqu²)|¼ò÷z«œ“…—ó8¾&ˆä‰Ÿ~Çõ­Éݺ"ËŒ%®ëo¹°¨ËrÇ£.BÜQ—Õ-nüDNw¿Ãu¹0ˆº¬vEâÀ(&¢½×Îyîv"VãÕË'Vº}ÏT_Í×ñx1ë¤YJ̱¬È y°ÇØtY=‚4Šº¬ÉVÔeõsÏ >ûê–"ƨ ÞnVððÑù©ÅÞ«EÊ8¯p·ÒV‹,Q6¨,ÆìQ—UO.eü:—ŸuYq{AJ ÈíìÅ¢Õá »üÁC›­Æoy¢kDàÒXÂõ "ød×H–›7‹F {!A„ÝvYx6©P겤%Ü–ìú|-1“íy#A Hô1 ÙÑËtõyÎÔãóëÅïÒþí@Äßo!RGL—Ýç¾Óþsì$ÈÝa˸\¹Ùû_UðóÑi#®SMWe}rñXWˆâ·Ú0Wß²bâݾÒÚ÷•ÿú1~<¨'WÇñD~}°Âœí%xÀ?‰ñªcYä`¸Ÿà½£Ï¬TÜ›#eÁ1oøwUy€iùB¬éÙïÁd‘ƒçþò“ý–XÍc0rs0"ƒ0mõû$Œ»©y$5y&áÉçßoµ Ó¹ŠAáoÔ’ÊVqÇS²¸)û…%‚Ž÷Ϙ¦ß +¢ QÍŸa÷²Š +½–¨¨LûMv50n¾&2_6K½ËP¹Ò¸ã1¾#ÅÙcO~ñMÃF7QåôÄ>¯,£0¬Bß[øëj'Õö™4’ÍqF&ÅáyfÔ™P£O ŒñNí⇖×,l³‚ä{ãܼ^™‹tåá ²qÉÁ$Ë_q×|™â‰nà ÞÏË óáfŠ\Ÿ3Âbš±Ü~FS,a¹ò‹¥ —§n?ñ8Oµë¯çGS›ÊoAÔú-£ +päg™¾šßN»IÃuˆˆóê¢Ô^ÒÎq‡‡ÉF iFã$.°hìÅL:iâP¯ªœ]h8-©WPèK¸,ÑnúyUhâ‡Þƒ*v”&CÅáŸ7ܽ +J­éê°……äïlÆ>ªêv´ºûíþ9×-ÜR²Ýé—1ƯÿÞ2Qòë5KLÖüJ&”²œ[ØÅLwË‚’!ÆÇ»ù@xEV•N)låˆãijCïˆ-Y ¾¬@KÕë÷Ôäú \Äô×¥ù/’¥éLyË̱aÂiE] +Y …MqHrå—æ.e?ûóZ5"üx—ˆôF _ÓÔ0?0©/O–ÀH¯ðé¦+àyÕÛäz€ÊdkJ‘‹l-Ü!ƒDÂÚ$ÄðYÚ“B†1e¿^L9³AJéú10~—Û6åjñsíü©%µ)æHÔ8—{ÓF“ïÀr䦞ëJ2?6Dùfl`CÔëª +ɘ hüªzé¹ØU¿M"G˜Hp­0Ó† ’Ýöñ„‘ 3ÍÅHn #úBW‹R½ƒGcw>³—èä0Fa}Œòxx¾U«>¦`ö`ô`Öb¼"ÌI„™›1F£OÌBð×™¦¶ ×ï=[î¼ èg ±öá7R|ý: ý¹=Ødé]˜—ëî +V°" +‹‘“¸_‹›à†CMXÎhD…Dãånpíy·-ZxÁöÂ_‘èÙ“ +o·§n©Åö‹I)nX¤î×Á +£j?Õ‡ýá«’€uÃÞ*k%¿ÖZ,´Á-™½Qvus% #?˜Y2+²Û@=@yZ49¿¡bÍ^=Òß2äÝú0¼Üƒ +Dbu½^¤ ÒäAê¢Ø@J–—âÃï V‹¸'û,,é_‘Ð#Ž +ßHöuúË|&×-~ +cnËÁx´ü5:{6ÄÔÆ!"Ì +MžTù½IÀ^N–&f†Sºgrz“IˆÝ‘öž4a?iá`Q×¥v*ÇuÃV¨]µƒšº¨ž8+^ù~$óH;NY¥t*k!‡:©xÀFg·É›V=Ž…™]šæ·(‘¨àЇJ3Ò…á:ž½ê¹ÒØäã®LøK#)çÆ5w§±iȧñ-ÍD¯}› Íd@2`0^0ï4´oü2¹e¾µï“êµåœFúµ>%ÚãªùÄýÙÍæR€™Thæa²Ù\ÞET~i.z¢ÄYÞ%K§IͽÊOuIbsàø¦†ö0¸ê¢W³nŽÓ.h7“+~T4 +›cŒ~²bi­§·ÎbߊMþ…Ê[”Gíæ°kví…¡]˺ԇísv:ÌÚ¨8nfmö‰fÖæcw˜®Ì9Œ™µ9G}šµQ4:ÌÚìÕ3ͮͧº ­Öæv¶(¿ì;ÒšxA/êôœÂ[0ÃåeŸË®eÙø(ØGØÓ<Í°L=øÆìú æ›]ë?ûfl~݆ Ø+¬É¬eÍ{sÏ!4rŠL®dÓ¯VŠf-ª—§ËhÍù¸fÖÞ¥å­IMž–²DI¯ +¤™½â–¶ù\/ e›Ïý³ló¹ À}’O1 jê\\Õ„e›½4Y¶ùÜ# e{ÁfÙæ¶lÿ`l +cIWØUJž¦_%,©´xõð²° <ÿÌ/ïÈÌ*\éÙ7¸¥Ø=(SY¨vínAT¿žúÚ,ž"`6@ö´&^¬ xnÚ_pÈ1aü‚C½z°0U¼£Y–цr.Î0›2ÎLª«¬˜+VÍÖB†EâRIo„e‚ÅT"w‘ÞÈf%ƒÂ„Ì:”—ú*vçM^' &\ºŠuãC!€Í +1ñŒWØ-NJÔ×î¶C‹Ø«>¼Æ£5*làLùLk*°ƒwS3ÀØe«üÚ¨Ä÷]ê¹`§O +§ù%×H¹@¶¡øÌîôJƒb}\‚õDøø +4.®ö^5A[í ÄòÌ´Ür*’¬#¯Å1ý$»Sab½‡Ü~ÍP‘4“5õÏ_vïIàø•…uK|‚Ú—U_D‰¯Ä‚êHƒn‚˜³:˜Ž†’ƒéâÈ´‡…dî1bºvW‰M{Šð0 jêö*Ý%îãG÷óZ£·\¿i4T°®ò#$_ú +ýbÙÉ®Žc*™ù¯Ï@@1Áb·J +Q³ßI·¡¸_)´ŠÝìi*›ÈÏâ§9©Š¢.5ÖÚÓ·ÒíóóUŸ•¬™ÇêŒýV ‡b™¨üz;k=–Åo¹#s¤v~3‹ŸbgcÔ¯QŠûf|wˆùïÈ7«þñõíöuw¬þ +˜êì—5²uºrºcÑY`r ¥¹ð•¯éÊÛ‡¤EÓç•Q|²a_}:°*'/¿3;Rj¥@lo0ø÷f¨ÜxºÜÀñ&cÔøð…vâ7굆oÖ*5@ƒß:5D)ÎÂ5ä3~Ù¾¨"’ˆÙ® (\ulá¬S™®èP<à¦;0H&A¹Á·[Ç9' +5Æy”qÆ“Y¤ÒhŠ”QT>õË8ø.Ù†1*N4³–¬.Ïà$ Ü<ŒâøVêÒbq;OdR‘„HNfO=×c|±Pk×NíËn3ÁÅ‹ª.;Í®j¸Ùv¼†4;åÄo2a«ßºøu7Ç~õ:Ð6U}ÑÐäÀž[ +ÀEY·²ˆ& êÖzd.Xì9¼`“ \-ŸŸú"ç&0õÇïD’ÇZˆŒD®ê.3ò¶¨I +À¤K +  LlÌÙåÆŠ ñ˜è°ŸOì>·#;øþᲃƒkåǎ\\ ÄÀÔ3¦’I€Ü8.B°vÙöþÔÂÂ41+ïR„”éõ> + >> +>> +endobj +5 0 obj +<< /Type /Page + /Parent 1 0 R + /MediaBox [ 0 0 240.944885 126.065628 ] + /Contents 3 0 R + /Group << + /Type /Group + /S /Transparency + /I true + /CS /DeviceRGB + >> + /Resources 2 0 R +>> +endobj +1 0 obj +<< /Type /Pages + /Kids [ 5 0 R ] + /Count 1 +>> +endobj +6 0 obj +<< /Creator (cairo 1.14.1 (http://cairographics.org)) + /Producer (cairo 1.14.1 (http://cairographics.org)) +>> +endobj +7 0 obj +<< /Type /Catalog + /Pages 1 0 R +>> +endobj +xref +0 8 +0000000000 65535 f +0000015956 00000 n +0000015656 00000 n +0000000015 00000 n +0000015632 00000 n +0000015728 00000 n +0000016021 00000 n +0000016148 00000 n +trailer +<< /Size 8 + /Root 7 0 R + /Info 6 0 R +>> +startxref +16200 +%%EOF Index: lxp32/tags/1.0/doc/src/trm/images/instructionformat.svg =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/instructionformat.svg (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/instructionformat.svg (revision 5) @@ -0,0 +1,310 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + OPCODE + + + + + + + T1 + T2 + DST + RD1 + RD2 + 31 + 26 + 25 + 24 + 23 + 16 + 15 + 8 + 7 + 0 + + Index: lxp32/tags/1.0/doc/src/trm/images/gtkwave.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: lxp32/tags/1.0/doc/src/trm/images/gtkwave.png =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/gtkwave.png (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/gtkwave.png (revision 5)
lxp32/tags/1.0/doc/src/trm/images/gtkwave.png 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.pdf =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: lxp32/tags/1.0/doc/src/trm/images/llitiming.pdf =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/llitiming.pdf (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/llitiming.pdf (revision 5)
lxp32/tags/1.0/doc/src/trm/images/llitiming.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/symbols.pdf =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/symbols.pdf (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/symbols.pdf (revision 5) @@ -0,0 +1,858 @@ +%PDF-1.5 +%µí®û +3 0 obj +<< /Length 4 0 R + /Filter /FlateDecode +>> +stream +xœ”½ÍŽ%»Î%6§Èètè_z‚ ôÀöÐðÀØvuÁ¨t{ЯïàZ‹”vf^ßóáà ‹Üñ£)’"—¤ÿvÝößÿúŸ?þ§ÿóþø¯ÿïuÎÿñpÿççÿÿçúßÿûóþø¿®úñ_>þÛGÂÅÿÉþäY>g¯­—×ß+ÏÏRçGªŸe´”Çgkë#§üÙÖüøïÿ÷Çÿöñ¿\ÿ?÷?7¦‘æg«ãã'úÏ?¼½æÏ‘Ö¾]ô¿½]—·þ9W{nO­}®»;ƒ÷§ÏþtÌ¿oÄHŸwﻢùöî¿ÈlŸ¹ÌýÑÿôKÖú¬©_"Æ¿ïH]‘Æ#ÿñå+þ}Wè1ñþ˜·ïø§‘RùlóèRgüÇcZ9ÚÑ©Îø§­ÑGíæøW½·çŸ<"šxkË?yDMŸ«Ôóâüe”-Ow;$Î?oËscã|„8ÿü³~ÖVÎGˆó¥ã2]ó³çs;ç?¦,fºÆ:M‘jnÿïÿõ£Ôçá¥k<·ý5FŒÚª‘y6¥O#m|Úöú(Ï»Ÿ§_ÆɵÝÓÀÏ,c‰ëýø=Î;‘| îC¶™/>ÿé»—qÊH»9ù3-5÷±léOÝ~.7½zþ¸r–§3ÚÓ)ÏW<ä êc•qõ¸Éw5²r=O ²Ù·^¼Þ9kf#§=¼Ö§c­i©€œÞt³Y¹çS;>Õäðõ²~¨‰{¥wý†.DyÞ]þ­~DR3Ÿ»ìQÏçÜ¢ ®oN½ñúÒÙ°žl„ÚÊ$c.#Ósÿeô WøYú4è^ü²„öõÊÛçÓ û’Q‚|=½þhZÞ¡®/¹o²¹ +mFºy9šûH!µ ôjRžê×RÁ F´›TP(óØFf¿º¯ƽÒE£A†YûüT +±ø§–»…~=d+A^¼žŸPt¹8Zt½TâéêEm~¾ +’ÉÞ +2Sÿîf=óûú6<]¥?_ÆþÉ+tº ÈV5º‘%q„Ì Uë1"§uGáŽèê¾PŽôóç¸x5õì±.^<ýnûüþ|É™Ÿ®Ÿ÷Ìgo˜†ÆXÿƒ FÕèO¸]¢yL~˜ +6­ñy&jkºDËò|XºNËó8…>÷ïÏÖ·àé-¾¥&|xóofß²©ñøt'Ùé¯Í˜Ÿ°…PHþþ&µGŽó±ÚEjoŽãaÔ9ƒñg3hlÑA4!ïŒk¿ÖïøB¶ÇBé‰üÊçãðŠzoãb6vóðw,}ÙsüÇà†=O}ÄFçCìËóˆGî×Ñ·ãzOÆïïŸÿëªIÉèg~¨ÿtšg{Bðç‚& +bÛþ~< ?ol0Êf¸Òù›ëcX`{Ÿàžö`<Ó(váx̨Ý6MY·‡‘ªº8=ZÿÐÏ¿I?.ç!i­Hâú–ÏŸ{N~7š „§_¼œÝ¯Ÿz›5mSÖò×ɨç­ð ÇÃ9Ž—§Çþï–™Ž†S£{×ùáºÛ;FO~{Þ~öéñ´û‚eû&…gD›i¿ÑèW8R”R®ûW¸#¿·Ò1îg?Û<,8¡wíü”Ü00¡¿ÖvŸda ? 3;b¢lŒôqaè’Äœ©FÏuŠíѱÉû{†ŠS +#ï*Dòâõ˜–ì Öb{0|h3ñ¶¬ÑYn6¡5M0ºjQ§~1}7Û%rF´÷ˆ@ïþŠñ¢3C½i.ãaÔ£nÂ{cÌ9ÌñoŒÛf×;ç6Í™°ÆD[Ø{š¤<ÑÔ­î4/¸èQ¼w¡o)úöe Wtö£¿õP뎾SwDÞ…Xlœý5Õ[Œdc˜X£¥;y4õ@ãÍ0_öLº CÞ¨hžwÇó1q‘¢OŽoP?¾×óçš\ñBöÆžHÓ6?¥æ6!pñZûàý\ÜafXVIjDüBÆïïZôÄé·ž;é„¿ò•Óì¸çaŒV¥qFW~K0ž²ÈÔUL Ÿ§l†™ÞçÏ2Èö(‘‘‹ºK¯c$=êcª¥ºi\É8)\Œ´JüŠ¹'ïEkK<úâåÐÑýnšéh™“Ls ºûÂ8{Æ÷ñ|ؾx='ùÑ6ÓhzQèÊ/»Î Ò<ïö~ñ‡{·áÝ»SÑ4×ÑöñËr@ÝÄ•&û?ÆPf8ñÑ7æ*©Ô\ƈè2òå‘/h¦”`nE2¶ðß5Ê"Ÿ‘], ÒÓZQÞ²NòÁJS9k¤´Rò©Ba§Žñ®0Agæ9‘d}ÈÚ"Oá•-È*`ŒÅ‰I%5š’ WÐœk þ %æOÏúœÛcµ_Ÿ’%uå៿I…[ÎxrÏšv¤“ñû»™ÏCBfËt3 ÓRiŠÃÔ*É´a_’*/Kvm¡jJº¯½í¯j&“Ë¥ª”M)[¨Ê¸P•áÙBmœ®Q¨ð:rÂ.T…ÛÞí~!3“2GUóeÊìŠ(s µÒK¹P«T^O¯[Å(ÔŠ¯¿$ÕQüó7Ù#Í FÕ¥CÆõ‡ùÄwb>ÇnzlNÇ|nHÛÈø³·3üg ¼@Dó¼ÌÂTÌ +¡trÉ& +˜nðãû§"?DSF"ªDbê˜vZ˜ŠžïÊ{(s¸Î=ûÝhnåÍtƒîÝ_=˜ó†mòæüÔsnÞ}=Ÿ®‹§7«¿ÝÒ-)šÖ8$¼åÍ¥„/»öþåºÛû¥ùWô›ÞÆuÕK¢õ?H©FøÎ{AQÌ«Yðœ2üþßHî$3'5òIÕ¥DÑmSò‡‘Mñ-Qts?1GÝÃ`º« åAÚ×½—&™Š Lì +ôŽi ±¾ù+9°3ÕÌhó±æžP“ÒÀ«ÛV³-Ù™é÷Ì*1Å`³È¾¿î¹D›3*¡Žù°\3²<Å]óŵ’¹»fûö71žb~ý$ùÇŠôèÖªF%m…ƒªD†i÷3®J.˜ÂQ:á„;~ö2ªN)s2 +Y‘ÏÅ6³ØŒ‡Ì’,:cªg«yê‹×Ó(^æ“•/·´ºæ“Öp¦Õ§â3«¬Nzz¶BW]œß™"b‚h Xù‚Ù·‘’ŸO3ÒúáåŒ+TÃn_¸ölqÜgåbŽ×£Õýv³KKä¥AóÚ?'8”¸ùü£Ýþld¼:!lä` j‰²‡„ßض{*jÛ=ß"å?»ë•î‡Qã#³ly‡$Ü$vÎÄÜÄè IÞlÞ£ôÐ +F˜žî}Ù­²‰÷'ˆhlŸ¼¸Ý +Ò3u¬4]`žjì “凉ÿÞßÜJí;~cº«öˆÚa;ëÔÖøç#jðV/ì˜lÆÑóâÖQ¯CåÊz’•¤î²ƒÛilÃÚlRî¡©gz2Ð\êh¦ñ{Þ–…p£ÎûèÍÈ`Yk/×·³òЋWDÛe‚°Á ©•Oud¡}úyñWKw˜\:4¶À›PÛ’ƒ[ã!qxR 79ú¾ü‚Õ~^@ +cMÄ42‰âëšj)#3ï­6›8h–+†¥øíá†ÄS¡¾õPßgdÑiÀp¹•C¯ícB%óù3ÛV1±~Ký"ÝÔ“9Ñ°a, +EZ†#ÈÂäíYBB&Úè§Íf =b0 HU¾v·½Gˆ~´¿~§>ºû}âJñF8삹C ±ß[4öÍÐ +¹LŸ‚ÙðEÞÒ8­r|ÓXÝè\4Í¡°såÅàÔ&énêÌ Ð*®›1ââ̘äËgÈPÉ”ˆfâϵõˆœÐ² ©†^Ã0†ì¶%Þ­ñ{<ÑtùóθOÓ€ +ôíy0^ +tõ¾FgZÂÄ··…›}p3°òñ˜‘™7EЗXUËô ‘,ª: K†W•zÌrÔ|’ +tzän&ÕŽ¶ 1ùeo«g”×]¨õékx˧†œô£R=¡…2®1Gm÷—9j08­zI~°!¾pÆuÞ“ea`,ùÆÈ'1ddaßXåÊÈC£_Æ@2×>náw‹Â/÷È-}‰´F[ÇÏž&+ZbÅ?:¯¡Í1Pñ9jZ}ìiË2ù>q3œÅÿô‡¤ð_ÏÞx×s\/Ÿr—Þ¶˜W=im/;«eŸVŽiÖe2:Ó^Íê¿Uþ¤O‘†|hÚNúBÀ¦U×I2]rh„[gùñU~]M©µ55n*›lÍ>+“ΈöTHÜ°s#M…®˜‘ KA9˜ ±…=¡+gÖY}ôÜH³(c'C Ä·ÒÇÎ4¡²öï4LŽXs¡JŽ­L Þ=4[WË6ÉôB0˜ÐÝ‘‰§{ú"ÞÎôF´£4ÚîÙ‘+>..¨zn÷AgÏéõÞ¯hÝõñ¥ã_?ÉâñÔ˜ã¼öÏRPéÝ`4¥Û’gzg\Ð#‹ö=?1nÀyý½Áøý½)¿®ž# äD«çÈ~ã!}˜XÌг|¸»Ø‡a:Oû´¹[&}hU##7ŒË ˜H&#éåpxÈ\TÞY×—›q¤ÍFz °±Å°I“s0žÑqñÚËoFnÖ.ÌŽEcÏ[ì[ºYÙÎzfiÊÜ‹¶ºàÏÉ0OõÇî‘Ütr,TgüÞè$šê¾ýV÷áÚÑËç?ÒÏÄýŠ‡~x˜†Ï”oèÆɺ¢*”cµ«£@Bïe#“›ìÅ¡›Hø™® |Átà¡"ˆ­ªRç>%}8´Å†ÂLàÕ–^EÔ홊逑>јI$C‘á—{"ƒ-ešê‘^B˦b´¡vâìþΕ AöâAëÚö™Jsx7ÍÀ¤ë^X!úÕD¡9[‚ò3£CêB“Gb°'ÅMd\=%‡×ãþ°'I‘¦¿'å(X42½‰ü¾´ÕGv«TV/‡X°'V7™*çèÜm»¼üam»öÅl˜éûfIãÒÂxÐÞô¾s±Tfg4Üp6àV{÷YŠãAã©(•‚NºÅ4· Úi.Šê>dfÀ¸NN4dz°òN»›—n¿ûíëž&TÕ;üÞ mNÝU—·†g£4[b: /Íÿ«¡.£iRjÜš¦zŽÊ–´yþœo7’¦>®§•åo7J#ÚšvÌ ³´ÇÍO_çã +”û:_—²W0ý‰Æ͆ôq—sŽ·ÛwßðéGÏ™ˆw·Â umÿrH*8lÚ–<ÄÓ¦/þ2ŸF[ÌgÕC Ö,¾%ɸŷÆïE#å¹}÷T¢î‚à룧ٸƒdbö`®Nw_é3ïçßÈœÄÛÍÍÝ6š˜h{˜ |Û® +Ç·óvï>;úoÞjí +)x»¿Ká×eËä2ýB7¸§Ó³™‡â l˜~Œ•2Àáó¬ôx”Ð+çÓÐ]‚Å@„û÷¢{ŽoxuÀ‚;ø÷Q•aoHR5­uõ ‹¢É`dàtìî¢ãõŸÙû5yd`«»wÉØÇ¥£jÿ|YFÀ¦‰ì°\A–_F„12Ñؚɡ©å]Æ.8ÐÿŒ‰^$F Ocý3Š¦fÌb%„jÔÈoÌGC‚,· ãŸ!‹"®± ?€áñ^.áX©2£išT0ëöÐ]jÿGÑðöI-šÌøΪ©ÅX£DÕ -ÛcàŽn¬ëü]$ P†5àZ/Iuò}V +¨î¿èÌjô r­¤ÑÄãÑ,!/b&r08yynÁÈÿ ×çÈïùÎðÀØß{¿5%Ci϶ÖF£‚È!#3±1–12--»¢ +ƒ-äÑ÷,Žr2Cñ;‘C$‡#‡@®u ‡ðü{8rdqਞ>8”–¿Ò'Mi‰<‡@"£#à¥;rdNr:CÇ¥—ýø¬Ï1Gýä1ž t ‡âó7i]¿‘C·=hÈ¡`üþ.À@2ÝŒJl‹K2m!“ä¨~ ‡$UG…Шi‡P‰:~7äP|•C’êG ‡B¨D¹P :„Jà õ +àP•À¡èu‡¡8B%rH2 äÐ!T"‡B¨U*¯Ç×Ð1 UÈ!IÕ‘Añù›”P…ÚBJHŒ9ô&UÁ€îûùêv ‡Ääû¥mäÐfl¼ÊPVÛÈ¡Û<Ô Ìm³³ÈÜ–›¢Uš‹Æ6tÈè‘€#2 Cûg€wn-9¸‡O?ÁCz»°AÞ´MZÓ_'cÜsßœDöÇ'¬¿ž9o[â ð¦'—“×þtÝí]O÷žãB…£_Ѹ·~g=ç›,;”V£®~%Îï¥,EÈëP§`ô¼ŠÑßéaøãl{RÉCÙÂjƒ&èö†Ñm›–›šuày€Ë{Hû.ƒ Ø¬ÀiÂLFÙT{ãÒ4¾*óÚ†ü|ZÄýÛª8ØE\h®É*³’1FUNu1¶„ žÛ½J1wÁúÈZ·õð¥óS-‡¿g=–"·,]‘¼ø8TIJdÇ•Ü’ÌB]‚q ”ó|¿ +'x0=‹‡2È›”ð Ϭ$¾@eãXÔˆK.h¼¾£; žy}WÿØð£èiim¼=®5Zæ4º+ɪþHuʻʥÞ7C.PJØõË1>q!Ax]†ðÃ’ØŸÃVþ@S`ˆ±äñ:龄es1á™ß~/€¬m,IËSÝN/‚nL/hl[寴âÖ¸\©‰-ÐMèúžÜd‚)]ž%-7f_žO9žrþQô@ð /] û¾R€÷ØŒÄÒßœìðU`ïÍ®ºâªäô¢B zÛ†5Í›‘e9ÜÂ…ƒhaù «|k$Ñ [•P¯Æç>n)íƒ# °Bó‘ÕØ"źY¬õ€ƒz61Yc²öÛùôLøP‘šNÁ^èSq}ãåªcSº5`4è¨LA_TÒ‰ÚîÄTÎ{õåŒíWëâ¬ã+Ol›Á‘dKA«É +8 úö½ È3 +.}Ž€ˆªaþÝmâiûqÃP)¦I'¦ê¤=L¿½™È’xˆ¢RýŽa€£Ø+€)" “v ò’:”|^`0 h—0³Ë¡ùý“H‡Î·í ÑùÛì‚ÆØZ1tq<ñwedCÊäÅ®*Íf •'—¸’S¡­[”¿¿C–)õÒ'{0P<Í[jÃ*»zËÍÂp&¸ï}Zm¥aX5 lìÃfÚJ½üµtTr +jh¤Á¬Î —8e5­Ò[3椬æ=µoÍ#r«úÖtÍvk,Nš «¯$#jUàEsÃ㦴S»eâmÔ8ÉÆùÙÁAOÜ\qkþ1A‰é³…Z^¾G_£†Í·ŠšfÏKXQ|ÝT·¹¹”¹åOÅ-]õu®¸µPà +è™ø#3®F!ÉÚÒ$¤1¹¢O3±LïìáXqk&Å}í·LD̦%"•yoÄ!‡ý7™bÅíþ¸&x‘Êâ܈% +·Påùëp\œSÅ(‰öáøÝj¸›_b@Aà§ZCõ]´ö¦bº^sWªoM€ƒ‘ñ~:Aƒ¤BkþØ ?drJT9«5n* +4¥È´ÖÓ9Ê­Sßm¾Ö<èšôÛýe´dY—gl=ÖPSÄP´yP4l›n†hl;a^P:!³ùâ»<ÀIl +­¦Õ·¬¡#,•>Ô§ÿmÜ‚fî˜7ã9?ØÅÁŠ[·¢¦ýy'(§¼ÓvÄí7r[ûñ— +m™áNˆ‡R ¯ ²!#<Ó<Çú€Ýª'ÂÅuqô®ßÚ}ǘÀÔlºW‡’–¹S?¡EŠ—ñèØ¢[Üž³ÛL¼íÖøôhÓ/Œ¤D¥4N +´ÁW³ýXò†LLóÓ…%ß¹Š ×mûªÈ9ƒ • ›HŒÉ’a¡Óh>W F“,^ÆèykF ÕiééŒÖÔ9]Ñ*öŒábMñƒe:²‘ ÇškRc¸fû1,<ŽŠÕhv«Ôº¹ù©ÈûÃ’á­"yœÖsÝãRZU +äÅëóæé“RË}·"E°·f^“ÖhSeºœG"­j$4ñ*É6ehZ•‰ß;Fíó¹f*={oð{ Tc¦• zëV窷´ÀâºØ@0à›¦<Ê3þÍd¼§˜+3tèa÷ùXÒ;nÓ +gB¦ßšÐ{à`ØÇYÝ:O‰Ø”¤h±ÚNÈtºTϸXÑ\ *š¶{?ÇVhÊÈt•5vFÆ +ÝÊÑðõDÕDÛœdFæ`ØŒ›•‘‰Ç{Î$^ÏœJ4Ž9˜h¼§dzÄ qAÒët;;çx<ûŽ)™£gÑ:ß8)šÿƒ4˜MµÁй9³©bh“>'‘,ur¢ÚÁtâÝ-ªÇ¦#¤-ñä«؛҃0ÂbüñMMðÀ¤½ªM\Þ8æÏíÜv³){Ãðý匲*"7Qkl€Ë™ç§r½°rä$3ü¹ì߉âÀó¸6N‰ŸgIÛ—ÇÎnk!šïâgd+±Ív|lÊq;[ÌXÇi=IûTnGgFf€¦‹¸Õ˸Ø‚Nr§?sQd\Ü?‘d‰­÷@&¥roÒ™õ™§@Y•»ÇY'©­œSP&û¢vôAJ¶Û ]›,•ÍІŽå4yHë naÇ΃µ½ÈÌ +ïÐëÚ±ÎÙ8’bÀðZYªR3L·¬¬us/C¼ÎèâûK]С;Ã9”Á +U%(îµ*þ ¾ÍTyGHòö+.ÙaZ?n>|“¹a’ypÌkyqÅjò\ÃG®aÉÇgö•ö®DÇ.Ä2Úo +ƒ´ùF…ú]Òz› +&W$+q^Û`åhôSŒ§˜_?I|”›¤ȽÏw +r€dØSNŒ–é!Nˆ(À†’R ¶âÁU)òQ4@:rÞËêPNc ++3ÇS¯Ï˜?®PBkxùbho +gy1Û[C +eÆ o@ ^Á0¡áq®¿–.´½6æôóÍ6 +,íŒNO¶”Ò"™ß—eÃï\ ™q×|»QS›&!沧·â›*Álfä÷!¬‡´½¤^Ü{ƒµ~­ÝÆT]ŒÂN 3–>SÜɇ'ÃÆ CB[.&Þnvÿ±„Më´§ìúÊEÂ/ÔRËnXƒ÷Ñ!¨ÓY®Éõ†óPïs·»Â¬)u·)yü>¦ÙÓm»-fÙlemnÂþ\\ð?eɶ§tÒD_µkŒ3löbd¦j`GŸÉYŸ}{Ö^(6 ÑWRŽ¾Ø·™mëÒDC ½Ø:Úl˜ÅuD0E6.¦ + ƒ;‰½B——p®9$yJúGásÅ©ÑÖ±WŒÎ}þœ •ldözÂh2ƒU–Ö:c0›Ñ²´ª&X0ZFeõ÷]'¯vù ¬ù”Æ/äQ®—²ØÛRi³í²O•x•mÁjŽ8£Û{I¹ZóåÔ[×l;°®0¥p=nçãQ§ð‘º”ªV²Å>¦sëõ¬ª§(¼X?t/|H¹ +/· +í…®¤ÛˆŽ}C’ùƒªÕš?0ì ÷¼Þ9™…-LBÆh½òñ½…õ³,†t¤ü`> +hÙ*}B‘øænuƒ"¥9ãb¹F&k–¸È˜iE5ÊÌ×ÄJÑ+M26 +PÜ}o郀%‹¶Ê…w[1+…­eœV ÂÛ ;… +Ù†G²í)ü–ðµ7“:ÖôŠŸ›룋Ff÷½/§¶p0#mNnòÌo$½±3Bš¿¿EÔWš_»=ÖJ®²”3ŠPËÐÐ"{Ú|ùRÐ+û‚QŒu.¼j4ή$,ð¤ªå)«`Ãܵ÷(²nƙ؜HÄ?Ë7rØö³jÞ`ÇÂ-Ž€:=­šâ[® +¶0¸ +Ë°°¦÷:( ·­óXP©´¥†-ªš^[ıԔìqòÀ®©íÞAEm\µƒ +[ýêa7søÕƒv®ìeÏdùeKÈ›™šÒª5´²v(†ÍاåÕ¹Ù?¾"ŽA/ÔÉsôUXpÈGÌ‹µ·b T'P’–¯-ž®qß8 +ÉÀþmÈ`ÏØ*²m×áO‡™P{$}ÖJ+œ“¸M¬>Å’U¹*Í•p5TxpQ—)™‡Ôj›{U}«é(•D‹”'§^¶[å^ÖШªâ +ÊT¦rÞsƒ ì&t‰©hÒÒCÆÿ†4ðõ± Æc¼¨ˆ”43Ô‚ð,`.û eÞ6ö™ÑÕåþÐõ"æÝu„ýª–.M’ÓŒÒjX,¬×í¼ ²7l¹ \^Û:„=Wå öÖÃC1ZÛ±[rE@€âÖ¶}ŠÂÝG€O¯˜¨ÚË“Já2§XÙ[?ÖÕq uë‰4¸UGŽöé5>}ZZÎl‹üŠ"!¤<4hX¡´h‡ý˜ qi³À¹QÛ†sj–`zã³ÚVO3œ( ìªãvúŚܻéþeëTÉ LœÅi™ÞD.³Õ<±À +N—3í™RSü‚ÉÿD©sÁmNÛŽ(8•Á>è›a9FÍÃH…MȽqÞn)Í‚…zh[EÓçáŒ~1•ö¸‘e=fe(¿ªi—®§Eæz•&ô‚™•7Šö›Œ+¦ÞF'š•Ê‡/Rœ*ã8ÓVâÝæD™«eªW9‰×y{¿Û6ùHñðÐ%·CÛh¥ñ¸Æøò,Ý£§ÂFú;.ùƒ®®-”ùúþzç4|Q0òiª$Eiƒ©Øä+Ìzlîe‚v»Àz“Þ_`fZ‰Ö šJé»…jV4—ÞôJa`ž,†m;Ðr$ +ò­ŠTšõüýV..VîÜŠå(Õ)*+…C€}SY^õqÈ)-†Œr“–Ÿ5Z­,T{Œ?£â³RÄÄX]­ ý¤j{ 'µ¤ÉéÂ\´ùäe¹Êûðå¤ÖÖ,_b©*­Å½|ñõ”÷Y™ã™*ÌšdÑTZ_šé¥o.gβ7òd­(¤ ɱ­¡ß³Ü¨HÏKµ^5ºÝQêhB3 vX®ð«8N…£ï®¤xó¬VÌÙôb¹ÖØ da +ÅÞ3Ý­¿÷¢±@Õ¼ÈÒ1§çõU‘TH¥)ô{[1/nXy‰VObš‰\Ô.Š5¼~L²QðTèE +Yö…f¶šš®)Šò—Õ™À}ˆ-z],ùùf ìßÃKƒPÒÈY¢ÞŽ¯•ð»XôÒ”Ñ4kÅ®ó9–6Y×ƼÔÛЕ˜?  à”í² r:ÝF}Dø8·$®'w—ݪ«_Á9Ôì~×Ã;Ôv”˜rØÇœ# +‡­\A¿¶OÓKXø<Œ¡®QÁ|Cãnú`ع>Ⱥ&||36Õˆì„vzµ³)˜¢¥d­r}¨†óiT•éÆ£žTu\ˆ3¾à\;ø^´’;‰nj)©NVš½‚á +¨Ðë'­Â²uYƆºÐC{¦Þbá6ÂÎC¶C¹çpºÐr;~qeÿŒh°uŸè3Xl=î×ÏEñ\ÆI©ˆ’hëÞ˜æNæ§Å³ärqJ+Z­YyuÔüZ ˬÎn‘xÂ(n[Ð&Ÿ Þþ©d`fjÑgÛ3 ˜æFk¼g°Çð|²Ø€i‰Àö‘O¤ zD^øÖN§*=³íýT»½Ž ª"Cxš¡ +MØæuG|Ý}àœ¦yéB“ž÷sx£¿¬œ û.‰Í&¼Ú(»ÞòtÜ[Bcÿd˜z•&òq#DÄ®· +,Äu…RLO]äxV…ïéL¡Ùd'ûõ;&ûùÁ~GýæJïì±··Õ +änf”ûÖØä‚qÝ¿šJõþ™wöëÔ×™nëa®‚ö Öß*ݲe8üð`X®¡{–¥`ýd÷P42l™û£#úxÉÙUÉꚊÄåŒãwfüf%¤Ž§³ïõva£½m›Dœq2ŠmÑsÜn~ÏIë}¼Þƒ£q&£ñ7ÃÇãëtò¸ûvï›x¼÷^o={©y_ºþõ“46ªÁ¼C;Q +f¾Ç:É;dG¨;n¨†Æ­ÕÕ`Ó½~¢Äبœ3÷†j89æôìe£ì +3J÷6?l'ªSåÕ€™ôF5ˆÜ¨ÿ]1Aã榎jà稞“¨ Ç6ªÁLj?Q +‹?Õ`Þ¡oTC{a lsF£ªÁwjÕ r£ÈTC›~&$»f¢ª¨†ÆľÃ`,7¬ÁH˜âœ…J÷†5c·$;a +b8¬Ád™6¬Á´§°ý°DÖLÜÙ ®Ù¨†F°½£Ú ¼+Þ6Жj0Æب#W DmTÕ¨“d:Q +æïFœ·˜°ÙR9ÉÔßP +{6ªÁb¶ Ê +j@ÖBO¬£ÒF5Ø í'ªÁ:¶nTC«~4$û¡"W} Lls£¶O)¿~üF5H=6ªa+3°Rå@5œ ¢a8ªÁDTƒéh=Q +mú:BT¢ªAôF5øB5èð5G5 Ð;Q +m‚쨋EK;ɬKH7H´B{ï{ƒØa'¨ Ë…hœ˜¹A +"7¨ÁwPƒº äÕNPƒ=½oPCã®Ì!«*DÝ®k7Í:¨A 5 òÝ ë›Ó€ñP¹J˜‹|ËÆ44_ÝŒ +€ÝÆ4˜R|¿åÑñ}£¤gÆžëϱ! +Fö€4ˆrDõ¤Á7Ò`¢*'¤ÁÆ×Ú†¦Ýâ'„àÛ‰ih\bï˜û­¦ñi€©¸‰‘@eªQí˜ ”ç‰i@¤¼1 +fˆrÛß2|1F¨ÒÀ¦–ŽiØ‚<ý£ì7¦ÁÆÆ81 +˜lœ˜›ÈÍÀ4˜òßÓ`öª¾a _»6¦&+0 +f¯Ú‰i0õhÓå80 +ŠÓñnLƒ¬“cÂ~9¦Á£cϨqLC(›cšö±¦Á—Ô`Ôz5Ø׌ +j°OÕqóS1NPƒ"®5XßÍ +jPÏnPƒD³ ße£7j-5(ŠqPÃþ`,î¹PÌÇ5 Å‚ÄØ ʼnÔ@Æj°W¬ +j€EÚ äýOPƒ½¡Õ50lÛ †ˆëÔ †@ +@¨oPC„¥54mãä ÔÔ`MË'¨ÁšÞ6¨AÊ(P]ï j°¾ïÅ ƒ\’Î8.Íßßã5@iNPƒ=6oPœÑ5Ø`UmÝ£§ÍÔЭwPƒ‘]¥~Zç\¤ƒt$·ƒXx5gmPŸ +jê ¤Pƒ ªƒZ,$T¥³õX1Ì Ç»VÌ$\Ì@Ö54,û Pòx'¨ÁúznPƒB†5DLá ¿@ ájhÇÂCÌ ›/n&¨é§ +jhS ÀÔ@7»1 +y7¦Á,ý½1 +ñ:¦ÁscdçÓžÀ1 +d¦~dcìÝóÄ44-¸máˆÎEû +‹ÊOLƒù½˜DSÓÐ|™¼ct ºcŒ¼L²O¦ÁtdnLCÓÊTMžM¡Ú‰i0Æ]7¦AÇ¥;¦©°Ó`\Óи}c8\NLƒ¯´1 +6s;IY·Í€yvLƒüa¨…ϺÓ`‹•töÍ £àñ= +V`ZlYLf¹Û6„5¤Ö²¤!lm‹Ðíž'¤Ám-! +~÷‘¤ÅçÒ`ô +Hg÷Ç@¯Zé j|9Vµ:¤a‚4xd HƒG®Du˜=ªÒ€‰Ñ†4 9! +Ö.iv#|S„4ÈJŸ†0ÜÒИèùÒP·1G’æ`d®«rÒ^dÝë¤l·Ëóö‡}R´ÌJÞ_ újt"“4&Øíàþ ¾Žø %*÷˜XŒÌi$ÆV\¦´ö*Ÿ1EƒbÛ-™Ü,ÎÍ´I.#Û•£ÊãÊ;bµdSêå‹j=¸ñe¯JÑé)šƒa†GµðK9.z•‘ôÅ¡ØJIVF?<+ã•ÄHÒ7PûyA½s8ïOÒ4O 0KÓ<…, +újzÖÆ×´‘á“ìþÎÒx¹3&ë^õÄOÒ´¢d‚'i LÊYšV4™¦‰(M´§iŒá–Á»fŽHÓÄûÜiyUiT3å…V¡¶œy+W¦FáÛxïÃÓ4Z+¾ó4&¾Äûon¯¾èup¯Q×»r,†M‹º³”…,ez`Þ´~Ÿ£‚I*ŠFÿ|'ø¥Áúhv<žžcŽ>‰ q³ŒÀð"xAVŸÐ%ŸóCŠïR~ý$x,„õÞŸ“ë`çÚñÜ›”{%-yýØ—Öe­M=çŲܒ6{iGJ7-—ãWFRíÁ®µ@ÃvÊ ðøÒÆc& ²rážårmŒ©q™p9Œct‚z^õÀ©êú¦l ù™6=R2c@Ð×ÖŠ©°ÃšM +øîbô¬Rãºvs#.cj +—×yq{eUzÆÔVÃÁߎÍ/Ð…ÏvØo3fRYmµ­7ÏÀ8Œõ–ŒÙdP¬Dý^¢Gý +sÁF©zrÑ&0ƵÄÛ=£¯…ú#r…0ØCö°Ò|ŒÝ\†‰¦Sê;Ž½ueSO@ƒz±yþúæZúžè-ÔïìΣDÁÜÚšXH½Ï´F‹&¾v“¯ ·Ž*¨yûÔÄòm o±_e!nf+’Í=á¾Ç´È Âòµ˜´Kç}ðÚ9Õ6–=Ϥo¿=y«¬¬ ç»"¡N¿Öå7;·5ÃåâÖ†ñISÐaÇM¬¹ÉÁ­Q¡ßM¯ƒîšãÅõLÊÄïCñ:–ñŬ _°PlN²bi:À2±“sõúƽƒ`'”Ÿ·Œrhñ8´øÖÔ)A +‹Ï¤Tp/ƒ¨µE¾Uh&åØZW 9|7 nnc4$ãó(”Û¥C³Ù¶gip]„óÕÖ5YŽ–w…p°WT÷.-ÐÍwxÂñ 4÷¡£0|ˆék“ŠZ°w>ñ ºq=&c;.`­£Ä3v¼)¨…O/T¢ïžê»!9.ãÇúü"ÑÝTëDù°º£msâ Û”àú³U‰œP´ {Ý$wç.0³ÎSrÄ °‚hÓ +3FGLZb¦JÚ€1ÂÚ'ć¶¢*Ž×XrÖ7ØYóÁJ¦kt&õJëýI'©(g3R C Ú.D[\žØ!¤GöÏUýОê ,ÖΛPÇ–®ñ<ÖÁXC航‚¢EθŽ{l'€–~b ßJáGÿ…a{gôëƒCYâ¡ß;Þå®êÚ>ÅzãÆï‚Ç”P'QlV0úN.ÚÜÜãè<ƒftU¤µ7BUE}hëÔ9w³½å™hÙ+<˜í=Qå>äø8@oœ&W¶9‹5þÞswû¶;fî?Þ3íº iîf ‘È]Rq–l¦}kOÎa]{¦@ëŠ +`÷\¨5#6Âø¦HØ_ƒ·ý« r×¾+qOï1+“ÞôðÌb´(M{šµ7õÆ‚åüûq0,‰d$ óÂæܽíú&ümo¨y]à°ÁºË‚û\#(À78­Ÿiü^„x¸öGó—{bQMÛ$þÁȃX¿Ýl,Ÿ’6øx½éhǪ·}úÎ0ñmú½©Ú§»wÏèé»çðvöë¥Ö}|éø×O²x„<4ð¼ÀŒU!' á`p‹S@ÀÚ7†õkV+îùÁÝã½Îøý½)ØÃßcAεFŠˆ1FŠ¡bÃÈ‘£YpI“[¿Ë,µÂ +Ë5WlÚ8¾5å|:÷Waâfœ[#m92ö6+ždid’V'Q¹=yx–¸ =ƒ·®§{zÇ+$Í +;{Œ$Ý©³–î· þlö~þƒ +ٕמfYÚ”›´s„Xlg¥B¾1¨ÃËT?0ZCUJ{¿+Vûã{»{4õΘºAÁM4Ùô¡zNÈ"°rÞÈáÀ誇Oã¹ñ”õ¼Ç±Ø ÿÎ|§¦ÃQ)KÿýŠ¬8öÅW™;ßÒ+)­mKl‡öjE|07Ù¹´M¢‡çªO;Ѿ¯¦r¬4HšxÚÝv96Ì„ÛêmS‹¬ {B[œñe¤{®†-„ÓÉ k{¦ñ^àÃýyÿ~ápeKrñÖÐ3m€(`M½f*p`ˆÜ쌃b¼Î‹Z VOr„XâUÀ‚óGï±Ô¼ÔˆŠ¤¬Ãi=t„À»AylL +B³3z$f©Ï¢'n!½¨ßÉOÀžÏv b°xä„kËÉ-ë™Ìy€Ÿ4MÑ×ɈVŒY¾1èkqçûíÛžÏ]èÒçn†M¯T2ÊÒÞ™ÿaj`¬O@—Ù°…“•KyåªÆ’¹ö_UÔ3pøXÄ8nÞ*XòÕóŽ)™5ì ™8fnu7“UO'Çó‡ +6þz€ivÓ¸^´|hFĻΠdénGÉÅÃÙmñò€Í•*ò:ÿ{ ¢ªþp0¬,O¬˜ËÄ5ôöTžrh°iñ9ÍMœnü®z‘mì]U¡}‡$øîèi¶ì ™£=õ>%Ó{§3…²_n«¢e2A+Ro2A!ˆ¸À³Q¼Û»%ž¢ôuö©µlËÁ›þƒ~]Ïx6Ò±¿Á˜ÝÃ4ëÈ4÷ï^v\Nf +Ðub¦(…XRÜb ¶W—ŸèØ\K¡IÇ_­ÏB'.S+ª¾0"sú÷üÀ «ö×îêá׆ଠÃrYðW¦Ž +Ýé?NÛz"§íú _…“¡Q±ôßM[ÜŽ»)<9Œü›ÇŒuM3ü•»UÖЭ¿@Õ¢›~¢ùý«ÕÏx«Üzîõrô~«uŒ·((kïë ±ub܉-]âÁwUðV;( Tˆ VsI½âc +ˆþ©~'?[O>2ײûíyë]¬ýÚá¹JÌ‚ðŒ³ ÒÒз +\›õñ`4šÒ±µù¤¹æ£aFkéêRý¹†ãjßv<À:¼ê‡èØ©`1È@ìkŸ½œ±§¾+Žè­h¦ø¤±äßZÖ´BÀ†ÄI[Âf&\Ìb‘±­3–®TžÆL†òµ‡÷ä‡Ù|+ueä.Ægů6Õ°;¹ý)V+·ýÒ÷4˜ëLùÿ}%§ÿ½º¿‘†Ià)[–O»( Íܹ•;áÄ<§çM %°{ +°Ê6a ¥“xlîî?È´õ +0ÞÞ|ƒ+í ÍBü!¶ˆ!d‚µ|ôÄþw–\ÐB’›Ìfc ¬&l-ÈõÞÕÓ5@‚˜Ðbý¸Pìƒ×a- š?SÜÓ ÐJR HlÂmÛ’Êk¢j ºð( £Fq48V8`Y€é•-“54Vð¸Ì6]®X0¨‹AgÖñ’ä1›ß™Ï¢:_]qíFOÙÚjѶ"%W2äf¯Þx†¾^[%mJÒmÉcbñ3/ž›³Žo žmj9áôís‰Ë¯<6õÎj…¥´•–¡öO9”´ôv$­ö°ÝžµÑ§i*mƒ$pÈç'‰ýº¬í\KòýD%®D ¯fÀµuÑaŒtýáÕt°MÙM›1ª5÷/8‘(m·Ü[ ·f¨¸Ž‹Õðj‚Çî!uzµÊM¯·[«ÜÏÚ_Û(;5)(ºµM›Ùw­í'Óù{á¼Iôdj¯»5ÿ¹5ÿT¿QýOU/%¢sÕƒ™0ߣ¯×]N`taç›"Ù™3 + +€èA›QmQãWÚR¼vDJÜðmí#­ƒþý­ O£Ü\SµKD¶,5cgyœ2m_™DÚuêÎS\†Èë¤ùEçMý@ÛÀÇ¡43õ7E}oÎ/èž>ÊoK®™D4sÈí-”ë¾+ GmÓܸ$Ñ 0ÑkSNóÖ¦=KæùÆ@÷Ò¾[{qC 6ý¥Ü)ÞÛ”µöuж}Ⱦ±öÒœ‘s|ù[31jPæ~ùÅÍ~æI"úЗiÜY™ÃÔc£äˆþC‹Þzz+v ,»ÃaÛ¢ÃÍò 18i$kŠç†9ÑãFöîŸ *zÜÅ·k«uŒ»{ÜßÊNõeí}4 ?q'¶lÞOfÀ¯µxc·Èün/‡ÿþÄñ©ºSý ÇF/Y”³{°ôöñÞ¿?v9`µæ`þýp:õ0i=ÅnƒÉd@q8d›-hÍ +:a­MAœ£àWƒCðN +Üe¨Fã™WöÖJ0{f‘H²ª"o‚¦{áLè6ËÀKBXØlµs?ÙQÜM|Éáq³ˆ?6œŠ]½@J²ôDÈÂÑ×ßùc÷§Qï½ûâÑ +û=$ië°9üAbæг:;e8®ÆS3´ËBã™öéU’ÌçcKµæ¡0dJèLç±yq~J7ᎌ…q„Rt cŠÆµþ«¥§ž¸ô†0G¸‘ΰžÁ "örÜ„ä(‘J…–ƒº²d÷„}éœ0&ny‚~(@«µá†¹K•i‚—Yj¯9Ý嘻­Ì"ÍíP¦Áe©ì„ËIöõ?kª#h6ñi¶Â-8\ã”ÊBAÈû¢ìoÊðƒv`ÓŽ–ݯOÙ¹ŸÝ4ý²°_i:ökßž~Óô›|_¿¿6àוòô£ÛY8]Ôaæv…ÑÃßÆ:*ã<ÎXÂ/кÁŠ)/¨„gÍ·+¬ÿI[~8¯8â›k Œc¥crh‹ºÑ +K‰eÌ|x&xÑ +ÍöÁùöH.•xâ½QtƽY¼´è\öÍ$m¸`|N•¿QÿÂwë wÖÃÐWw{»¢¦ã +°hj@Æò®¸Õwwöž8I.O +†A.|”¤Õ?}è#㢳ɶ¤l´„)—í’”‹¥ó¶KÚ"2nÏrH¹èˆ“ã +v¿K¹tH¹ ©B.ic 7X.Uô¥;øÑûÍ™¸2&ÕÛòxÓ)ã¯âzD¨uê°$8=Ý8Í¢f#qœ£Ÿ'À2š¾UhL¶8?Ë +Y\`Œ6¶=«<î°x•;î+°«Œèed»kXaÞ`X;4Ê*P¶:]ŽÌæÂöµ8~{ [ŸnGÐ0,i´Å± 3iÖÒŒƒ#µRF„ + +a{,å`°O7§°‡ÎJÕ\|é’ +Ê/Y—„ŸøíY‚œGg `Œ:Þ~ïMªaž¤ƒ®©:¼£Ð§$gdšÔ¶¾ª4:ìtê!…Æb+¶:SäÔ_PbœÁgX#gâ€ûd)Éæ$=sâ;3ª²ñÄ,¬:êF£²–àˆE¨ÀèL:j…HîcŒ’%b3 5É'zVí‘p=kÔ [G@ÙÐÚôÁè9¤T.O.u„æˆØ6ƨ=4â›;6å¶>Lœ‹ã§O(£ù+ÜGx+¸™)èÉ®¬ŒZ’ ü}zdÞí/ë¢ùê¡ 3‡øvÚ*6åÌïçÇÙ +ÌCE+3¡knŠŠ§'îZxð\<2käî·fi†·++ÄòvgP㌟?×L›Ÿ\ø/ý—ÞŒ ({pœ‚]Rå9?h.FÆ”5¬3ÚÙÉ­;ÆçL1`Wx +è °ÿÞnä @fÛƯÝRR‡½Çúü +”Fë¹i¬ñœc6 ƒ­¥ÂÚÀƒí"LCÒù©°“WõpŒ›¶ïà$ R8Œ9Û¥p}N6»ÞÏOw7à]µÅâÞ06¸!H:Ëût3AG1?5:é'¦üȶ¶rœ~bò ÄĘM#ܵ,`à#‹nÀfɧU6ë]­¨ óÍwüý89è/£-¢Áh“g‡uc”`—M¢!cÀé0tbÖæ-îð)NTç¥+äÒ–†câfÆûµ*蚎yYÈ(zÐôµj"W¥ž2WPW§·æl¼dÛ^3-à•oÅTE­‚êp6iŽ-ðDÈ—I×FKÄÍXЪ‘d»ÕÑ°7é¤A•BÙ–¯ƒŸ:ïå6åzÊš1Xÿj¬ZúfË´À•úÏšð¢»=^dàP\Ã=Úqp äÚ ñkè´Ûjï‰gñ|ñméWë= œÈXC§7ÐÕ¬hÞRýXb]ÀBZøY¹À +ù0N--ˆsÏ‚Ù­ŒzP£t%i×Õ¥½<÷[ÂÖF+:'1È`3@Y´l -­uï”G2Ö°º-§–uýBùÂqÜ%ÅΦÞ؇Íœ²IwvA¹kM3¤ÒÖvÖ4³þ¦jÛÌYwÜEm³s M‰¾Òµ-\øRH`maÔyEu‡Œ¦±²¿¶ª‚·¤º9¦õ~Z=r66 +ª^šØ.›t5oWáÀÑ—XÌæHò,–fàX»h½QxCžrš +Í2Œ‰ú|é­mZ.Ï  '=è™É¡‚Ý7N¥æb¼£S'}¤Ð@KMâ˜Üñõ–?Ép¬\ž»xVl¢· x¡ÑŒÿf|{ÓÒÎ…SZŒ´ÙH³ˆ&e úEËDøPÒ¹9)ÝBøwÞÕÑÇTÇ©aÐe 'fÔ¼£!²6Â"Z¬ÛtM®Àαs@Û_3#¨ºHçÍÎl·_`y +•[Ф¾€Ç!)ýmýz»¢5]2iPkÓ¦÷/23÷­¡Èã@@7j¼i +hN Lf´É·T ¹›'@Ä©¼}†í@µØYì_"Ç@·*Ï!Ú}’µœÎø#†æØß>Ž ÞôBCAõ–•ëÀš+VI˜Ä@³UøˆêÝ=>Y"%iPJȃiy[hÊRìÔ(ÔÄ+Ü=5ÅÑÛ*)žpéÞÔ­Jý\ƒN›VKËÔÈKlYêÇHìšy‘~…'ÝW4î@†gJQVU³†Ë¹ê%RÚcÓ0àÐ îÒC34šqt +tC_f(èÆ"í§rE²YA2dÚdtä_ŒãëíŠÞ‹¢9ì/g»U.và˜ð!s½©ÒV¬Ÿ”Ï I‚žØí×0)DoÇNœ´vÒ…¢7ÎÐäÇv&7M@§õ3ºŽÓ¿æ´gt¯ÙÊÅõür–¦]ò¯gJ +mm“Ñ]¾DźÛþ|³(N+sCŸ˜¤ÅiEÌ\Ò’¿ñ ¦Ï}ÌÑ^F79¬¤;´ÑW8¤)å¢6=#´‡VÃ$‰GEY“t×ÅG¼REìÁ4-ÌG¢¨˜”C }zšÁ0‹Ûá +wØËÁwä›È†œìZYmXëú¬”Bp4årÍس2Ó8;~qE,ö—-Ë›ƒ¦UM¦ _ÜN5·†4™;ƒÂçÒwÔ:øu´Ìí2gs¬v·§ºù7?‡·ÒYxÿ•ÃA•»fÙ4)©»²âŸ´S\’¸3ÞiIÅä³ÈC~rµÛZÄåAß*}'áô©=Ωү©Xh£±[š´Þ¢aĈ’ÚÄþC³né´Çä+jf&mXH&R4Wú#-®÷ì‡uÅØé-iÁŒ×ªaæ¨Æ<ÇúÌÔ¥†®#VÈ8tÃ%ed¸qI®pÏã}IÁÊÎs +h†íË£ž¬µ|‹g…ÍPGR!…’§ +N$ ²­ù‰©>(i^çqêZ#Ž2›cK 1™ƒ=-Øð/f¤í£'ÿ"Ÿ£b0—7û€N¼Ë6 /°|Öë—´¢'†`'Z ~“%ÈìÉ¿œ_rªÑí+œ…­˜‰µ:,¢¡³î7‹˜—*’Ö›Éå…|šKÐM| LBá.JÇïC½>p`“ÔC2o†‚Ò#,J(ÜÓŸY`J:G?Îa‹0™1xÒ”R%ÞâcÛöå-œ·­ +ZÝ€ƒ6cúDîàhu÷³HËÁÇG¤˜“NuøÀ¼>Ïô^¦UãÔñ:O>ñaçÜ[Ç 0É0(]äòT8²âd±òäl‹;ÊÑ%-¥iȤÙCp~èPA*ñ‹RQ=—‚Œ­h›ƒîÊK©„‰Í)AûÜPÉ,±5Ç¡øÜ¡…EV¬Ç-U‘ j†M”¿*püÙfJ +u–îÀî0ŸBÁzr7Ÿ!¤èBSWÕ’´Óë ¨”}jNÌ@oÝ1¨l ¥ +c×ÆÐ*0±3IŽ›+èPƒ”–Öªž4!2PxöÆÔõÆ—»)Ö¸Ô@DÏ°q2uØoûÉÊhZÿj¨"B]ŠÞÅþ³*bÊ–èEÊØSõˆ_€ª‘·TöÜŒR^òsÁàUlZâ·ýÄáîH¼Í^¾9¿¿7ÈÚ8ÙûlÒù÷cs¹mA?ì’åì4•éó\ôð$ŒÞÍ„0vÔÑAºÊÌEjaT¥ó +¬ê·ÎÃF¶f.-}ºÛZ,žM”ì¼Ù¥ PdGJ˜#œö²%²<Ñ'{Þšè휠ÿ!žË'_ðºS6e‘ÂÃä`¯%ŸHíÞ#ÐVȪuþ‰ûÚ't4 +ÛýøŒØ¯¦*ª(ÚxéðTt‚2Mâv¥ç å …Öñ•’b½g1ÅÂÆBQÖ:Ôñ,¾)ûb²ê;‰kt«ãÍQð”ŒîŒq„)'­’@p¶Õ}s\Øxi+ÌÍ»x¹`“EŸ2™ðG2°ô¨"ÈÄî4/»‹7aù†ÑI­PƱ0·ß3%8UQhp¿-Ú\Kº$ÁªZ‡ÂT~‹!ñŒ,»Ë¤†R'ªØ4*: kÍ&zqŇ°„ú•‹Kq¯ÉÈdSÒåž×£¯òMÒêa4¨ð KJS¬€®þM×1þy´+ìC{“ÌDÖƒÞ&Æü:É+LÑŠ_ͧÍËwàÔ9£Ë"Üî² Pá¬jòŸ!'«jMˆd +:ÙrÈ8Õ›Ÿ&8Ò‰ >¿ÒwŠ @ïOÊËÎÂi[ZU jÊ£&Ú’Våq‡þ«=dêiÂ>õòÖnÒÆ›½4´˜ÊMðvAß R^Á¢Ï\„\É#‘d”éŽiëJúÚæ.°];Á€f~(kÄnMÅJݸ9¦,†pwa /}÷­˜ÐÖä¢Ó’ híêæ· +¯ bªV]>Ñ÷L>³˜•o½•¸‘9µÎWTÔ¯¨Ã+óÁ®TÿŒ[|îk»½;Š}# yÊ\º^3§ Ì˳¿™ +]h8°j>±c^Þ×Rýª±¿5Áªâ݆ÇÁVõ3-oÒÜ{Ëç1¥Êj@È–ë¸äæVóxÃM‘•æð&ÍHQÅ +@oˆ‡e<±Â_-žqÚÜúœŽ‚¸æ®',]ÞoM*¡(Çh†æ}ïÄÏÜpz^úüûqr2q»ECÅû2é¿*ÿöíY> +zát½¦…M%höxopÆi +¬—ô‚2Š1áuLÄLB(FDdô V¨rbœY4›ë´^ÄH¹¿ÓÓÇ9Ž• z–ëQèò|–nóY»”ŽØµyªN]Vg§xe’±/a¢Rº‰Âršì*΃çš:£T-¯kê\ÛvÜ6n7)mÎý:®¸ÃÌ‚~+^O!À;‚ƒ’ê¡4‰Û;|Õ«ŸuM°R¥õ ¤ckd‹»¶õ÷õm–_u~/úÜ4Ó–ûåAlÄÙnŠ†Tª×™>çXÂ'Xë›:}¿¥àíhCa37Í@€ F›àO¸k¼Ë@P­€NS¯“‡ŒÎ€Öð*ÙèS£¾x{D‘­Û/Éʺ »·ÙÈ7qPjU³Êáõ…ƒ“ý!*^¥z¼TÅ+PL§£ÕCÃß¿k„zù§ï+–ç31ŠÞów°swÚÆUÝï´ÛcÉnÔÜk<¢cÕH¼¡{…8áNÕÙyÚþŽíeýK•Š ùìG/ÁÇK˜XQ+j ñØÈwaü, VM³5¦;ÿ~8'f™UZMªp“C`ìè©Ñ=Q7zT Œ +Å=ô"²Í,÷º9ótì­-÷¥éƒcY‘F­#È#a{vÜVxŽIüîPEe­:‚8+¯À&Ò!|Ãõ&1«—ð|{BS7Iï5°~,TÞ‰ÌItýd*ì‹((²þöq?NÖ€xÁ×£ž®(Çòj.®¸e;TM5Ñ;±¥M-u?ÝUÛL¸Ú§6Õ'Q}´SÝTDv÷6¸y%fø s€cZèÀ‘ÕªƒrÞ—Ì€sª‹ªk¼Ž›ñăÁ°o¦&˜ºÆÖ„Í¡¿š8dMš1{Hˆ³xvi @vùeÞï“#bÎ…t½¾çRTŸÆíá%\&¦#ÒLlâØ3×Ý·¹œ^·«ÂÅ=‘ Áª"¿Õ#Ã:”†Öº^_õêg]{̃­6@¸ÄÜÕ_Œ%Ê>ò[Á‰X»YluÆ%ΑKÓM?q<%§wŸ8ÏoÍ1 ÷KÚãÊp›Sß(“y†òEô)XË`M¢O¬ôH:¦£¶Æ; ÞâüùâœY±¦¬ìa$PèSÈ, +«„9|FÁ6Bo°e¹ð>é­’Ž +ý"&õãŠÊÍ!;4õøÖ!PVü·}5€¤“w|¼ϸÞ`qöHì«ÊÓnmû´êIlvmë9R!ƒ,32e)ÆK¡ñv vå5º·ÝOFWáì/ìÓ& ¥­U5Fú ©]ü%äl¿ñg+Fp¢À¦´Ö[aöÄAãª_(!lRÝõzIÊÐ.ît +ø¦ÉÒöƒÉk·Äô7¥·¶%`¨:ˆ l4À/’NJýøkîþ^l„ÍÉ©m¶’IÚ¶hΠ¨pB\Ò˜`´· +y•%ÜZ’sKúi +¡e¬;lËñTßè­>Îq¬»X‘(Á‡WŒg½%s8C˜Ò(t aSIã{z«€{7îyÔ0YŽ‰¿S:×xæ’qn>sœR†\Áxý¨¿€AT %ú+Ç»¢9'¡r"Ž=É9¬ÃcgÛÝ´=Ø0š,»‡FSà[ø°XÂøÈZB[ø¸YŽæ¹ÇqAR32ÖŽ“.p@D¾tNt\óhuï[zG”ŽÓ7òž¯ÍÑF6x‚|lòEÀÐÌGϧL™ù%œCßÜ;c†‰Ì»ßŒlì' n:ð\ºÃÆAgA ŽßŠ³t\‚8Cž1ëàkëN>­}â¨]X}eâšì'Ôv:Êâx#œC@Š¡%^¡7Š©¯ÍÚV)J“õYTì±lZÛù±(Åtƒ/µ‹_C„×-iØW–6%õȇ‚_‚íïí7^˜þÀŸN Œ}  èEE‚QÔlÓi]Ðu‹s¸=Mâ¡ÇÃ! ؇D^†¬±*ûKzœuŸž¥¬õöfq@X,ÆVݲ¡µïïÂi(úð1d0Ù +il ¿ëÀõ/Ã0:‚ûÊb ppXý³9¹1Г& +ñ ™i]ÊAQ8þÍè™a5z Y½Dˆdp3œË–›ÌýÍ ¶Ò1Wž$§åE„ͬÒâD4T`sÕùpÇX­*ù%2¬còŸH!³à”PYlÇÃ=zÑLÝu»}Hâ;çÇNzÉâð4ÐÐl6u‹+`%µx€£Y‡#ª·_›C™‘ÈU.ƃc-ë ö’[vE$•»RÒÇçwnðn1`!Y¦¬Ÿ á½?dÊ|EÚt‡¬8y}á•ir„­q™VǬ} [dïÕ`©8£ÜÞ#Híea¤©Â{  4ÖrŽÔ«ð÷•0ÜÓqT¨5›å-„^U]Q³ XX¬ŽV:¶)+DÆr2ÃTéÛ‰?)a‹ÌKòªÓR*Ö…B±“ NZ¾ß9!éß? â_hÐúöƒC¼¬NA€¯Jºcó,0¨¹»S†£çÃÙLÍERŽÉ|\:ì¡‚^aÉ¡#ÞÒ)$÷w@™´›ÍÁA|BRt—¥Xͧ~l~Ür)ZqŠ‡–”Un|;ÖMaËÄJ§®!—¡ÝØ {ÃS8¬Ç IŒI·p+»E` +]}g:N\¸é$„6ùˆ$œÛP꣌³®£Ôx¤ºÝŽB˜ýŠi‹aÝPåµN´Af´J#”)Â’v0»ð(gÀ…ù¦qÎú·ÊCe­" +eß1.Yö‰®îÒ}…uVï§7):Ã:®èÊudeÒ\$:à%ØÜi'Ã95¥¡€ñéšPgwÕvE~ó&%¬à-mîLOé_½Ii1'§7±–ßmûŠ¢øp{;_b¬ãŠÊxÑŽ~Ãæv•ið³Ãý’›Q8ƒ"ºu‡bjaê§â0Ö`ŠòŽÜšÖ8ãÉ~=§ +<™ºÙcú‚`¾ŠÃÕÕyìñ\”®òñ^t&ù¡šÜVœkËT6¥x<6§ô9º)AYÙ* Ù.·l4 ßF7Ñþ½æöˆî\®‹Ó‰²¿Y -¹g%•»œ²Óö*rd¯¹##÷e5šY;žÙaæJsmk RlCPC­#éƒ)¸25`G¨‡}챆Ù}æJ{ÀqjJÒC Ýbw–ãq.ïèxT™Jªs)ó+@rÎ:qé +@Š=¹ß1X^~Û¥\}Å`RݪÏG´PtŒ·h¶”ŽÖ—ßy)Ü25#e÷ðœ[ÝÚ¢€®ó>ôNsªE„CŽò€ç¼ä›q/A÷cr&¶«ÛôÀè4OJÙÔ¾Ro·ýÁúáK ¬v8 +ƒ¦­$¼>ˆ£YoW¸ÏgÕ鹃›Ìz´Š;Ɇ+©é«ÇÆgùp%°Rr®Ä°fš>É•Ôä¹â.x•ûzglÃvp¸ó¢å—vE£¦ÈÖÎø®ip_bß¾]‡‘>i+±ÞœÇ¶áÒ:<‰Ëc{“XkÛ“˜DSÛž¤ÊcnOÂÝß\ ’¶+©:rÕ]‰íp7NOb +SÆŒž¤êX=÷öi³ž¤fM üŠô©‰‹ïvÇÙÒîn] 7â:÷#U9çð#ƨýp$¦Xéð$Ä9mGTdÝŽH©´MvÅa®×áG h)ƒ GbÚžŽì?v +œoŠY"ò¦#q‘Ê‘h¯ÀÑÔ~ga=Z…üÞGòmdïíõþul©mмË÷îz[çfTÒpY¯Cõ¹ÞÖðƉ¼Â鸊s{½¸B›ÌÄ[ûë%¸¶=€ŽÚ`%œüvÇìS*ßaSÚ;ìáKpd^Š4ƒ†…vØ‹ª¶ÏÃ|‹}†4Ô9](Eí°ùGî°Qï¹5EÂ1šwd©hW$¾ÜzòøÍãÏÊxÎHû•C$å0žØaoÛNî°wª¨vØÛ¶“;ìmÛ9¢ðæ¶Sóÿ0ž#ÂrÏwÕ:÷U;Õms8Äg„†Tý©M qm°§1þáìí!>}vpo‡øŒÌ6SÝÚ_ïã+ŠJãÜ`oñ-#u 6ØçØP­°;œ>gs°©6ØSªãŠ +ö¨Œ9T‡Cií°‡¤L +ö`N‡ë6ØCUã^±ÁÍþŒ +öŽ<6Ø‹T‘6Ø‹JÆÍÛªÏÈnFVÏ¡ÿ¬Äf,j™b3œGtåNŸ­”©qüèZh‡1pÖ«v&MSG²Ú>¹~‹›è +ŽÎ¨Ç²&rü VËÚ‘ƒervÌ­vòI£PÏg8ÖÂ×Ä–¥ž4;0Z`žJJúÅ:³NææX->®¨uë[_BTLžtªÍ"x¼g£¡ +ºÇ×WUí¼ ”þyСŸmÊÝ$ +O7 +»@}cß+M”š'÷æÐæññˆï›MºAŸ¬ÊxoMÀ_ÂÃ`›Éé@ì:¤C±Ñýf¬ +Ìåǧ?¡ [e¢Ñã XK¢L’8OèèYngÔuÒpE÷¼$éBAbosøØ©2m-i|pÎ@ñ‚ƒ/²³Oà +×AÑëñ^n`ôÚ°‚Ó…c™¤kv Ç Þ’îv\Ò>ý‰@….Ñ +`¢½yŵrqv°žà´+sÌT`Šìá@JÅ·Í|©i45–'C¡eMÐ +œb—?¿we¶jh¨ <¦N¨vœ’ÒÛñb³¬õ&å‚)Œ¯•ã×[P«þîZýV3g xxr¦Ÿ’rÚ`'ye½qk +TÖ]—ï3áúnC^»n`a©ðý$.O<ÄZÀE½ö8¤â3ò´á5™Œ@‡'/B1à;šv;hád‚Óf-˜±A€ùwN À¢Gƒ”jÓA9…Xœºu®rU«Í4®} ;Žô rÂOÐV‰Å`•õÍ5t´yF¬Â˜°Ï1> B£éhz¤ + sÓ6Áø”Cz yÔ°âBï|¢ŒP‹€)çT@%¤(Sj x+6¡cì8B²yu)KÿrùF[Ô ‚l“ $¦)>!5<ѹr^‡Mzèȼ¼‘ÔÉ;î@4Ä{;ÈeÍ©zjkÍ÷ +Hš¯àP¦¢[8„;QVöR¢ôˆ&•Õ ÀÊ 8ÎʘLF=>lDIÄŽKðÑã]ʬŠñ³rpï +Ìàl4æ®ÝGÈiØ<8˜m]éX­¯e~±Ö +d•0Ž0°f«<Ó˜d²O³ç8ûôL`‡4¦E·…Òœ4–¯Ì‹>BZ[…jT†Œ L.$Ç“Óàß97@kÉ©²N;?¿³l±`Ó-ú8¢T®i5¾¥‡d¶QË—\Ó‡§¿WGfØþª´m쮵ï4”—íE7á/w>‚Î\Ó*èÌ<ŒZªX^|éM”Ž[qE€ +ñ‚Ô]DdAµh%Ÿµ3P]Ñ2Ð̹èÔúÐ +7Jšû'i¬ÀEdžÛñÏ7Ò+ÚÎqaÿþa ©Ñ4p³5œH1ö•˜ò|¬¬ú«vչд¨ê«±£µW³É ©4ý +À”i,°ø6júÆhÊEvöŸ ¨ž##}ëw·ÊC»nk±JŠÔT˜iå¶íp¼êÅZ¼•»¯D€Ø5"btáoŽÏlC…k¡Z” FPdO +1º¨¤¿#›%£¨ß—¬¾f±0F .€°èg²>¿|¾‚îµL<–ÞýLýt…{"0hIA#<‡(Ö.räù ‚¬·ô€Þpüî+šúŽ†v~ªëÌ”è%Uw¸Ï+ê=§ÝÒ8§.a,né„vP¢.wŸÞ8RÆ8Y‹›zæDÕ×”fµ+”™j‚97Ô³LA.²\ýÆC–dU6 IrÓTÁèºOM l…#&¨VáƒÉt0îê ýälLj³œ$ƒxá e,L¹µ±ðÖœÈ7GöÔwEZ +7±7N^13Á‘öRϤ°xo¢Iý\‡uÙÎbLÍz¢Äë0â3"K7⓱Ҿ€3T‘ÉjÕÐŒ¼jÂ1ÂBãÄC`È"«êජ‚¤1¦º ¸xÇE¨9#÷àd¢Ö±ÉdB«é<Ú­l;6™Î261°…b +»¢ùûpk@ªµ*Õü+‰ônÿ¦ñæˆÒ «Àüþ Ó}ÊBi8~¤ôPH˜ÊÒCgåG¬8_O?R4a—);J’1$B>üˆí_3ÒáHŠkáH Ì Ñ GRjŒÞ¤r¾pG‚TAþÜ‘”6ŽÄp²"r$¥î¼Áän†èI‚žäà°{´ ´{øÙnÞ.Åït%Võ¿s¸ŠÒÔJw%¥iZ¢ßû§®N¸Û“Xÿ>È9ǃ*ˆçE +«ÿ’Òx š!R4 t´”Ų˧Š)±kgÌe½óWè¯àQÌÒ⌊g²ï +åÊIû±=9‘€Ë#SËDõé}Ñ*¨æG—.îáo4d¾" |ï†z0…­ÊA8"Xp<†ÌÄÖ(çÀ%m¥Åe¬Ö¬ôx™˜)—?Ë›Þ&f„}Æî6T%1)ØßßUíöKa2ÆSÚŽ“‰”v0"½ŠúN¹Ï”vÕz@OiV£¾¥´±EÒG¤´ +zÑŽ”6¡Û[>²Â[W­æðœ¶áSò™Ó®XÅñp¶"#¬œöA+§æ´í õÌi2ã~Ëiãrä´ut$D]‰H ëXÂâá§_áÏ„û1 R{KióXT¾ƒ{Å`ËKòtZä´+Ú)mkÉ<’€Òh‹2¸öbß)íš¼RÔ¶ŽîŒ”v¨E¤´ƒ)m>I“9/á´”œ|¨‡ëmC|j ó@ì4å« +±3ÞSÚ•Õ‹}I–“kÐq«£Î€-êdOiÂöám§"âÝ ?Í“9ß¾£D®e̸)u‚(ÂèûÈg›âä·|6U>›ê^ÔûUУ°Ü5Ž™ªøù&egÔ÷¦×Ïzq€‚þõTÆ¡6Ròyæ&dç +ò*±Ã‚v•8`A–û†ùxÞ®*¿‚ô»ãbNˆ37’¯c‚~$¨ioQ‡ízh@‚šN¼pHP:1ílÉ>A3“ƒÖñ>¦å8tÓûêupÑ7 ò\· AfTöwÜŠ Šo—‰¾ +#LÐq0A4*&!Øyê&ƒÅ˜ s8ÚÝÄëynê¤Ñ˜ ”'ÚÆ)wþÚ˜ ›ÑævhB鯊õ9”mè0ÇÙ4š4#b÷‘á  ‹`Z(ÈÒÅ«(Èšw¸¡æ·B5¯¬8*(‚?ytè  `!©ÔxŠs@~¬«ôVÍÁªGq:t€‚ Ž¡;œ-©S(ƒ3µá¶-@AMÇÂ8(ÈE%3Òж(œ¥¶AAniö·ÇŒÔAAMO×uúÖM§ÔÖî‘›‡c¢¡@зvcØtÓ~ +¶5‹'*P¢û†òD·†˜/M·À³‰#8Éà=U +'å¶É¥lo²³% Ê(‡f{2æ† »K¸$gŠv LœyŠ|}ÄI8qAƒÁ#uÖ Æ†>õE´…¢á1’6ÉÌ©¿Ñþíà0"ëÚ3£(äJ`†l!XœÃ´<äªk#€WÃûì»´©Ûþt€`ÅÄ +‡×Vn’ųa_jéär@SÆ{ÆáIõ +kÅt¬ÎÔ¾z­rS×p6,&dZBž!è­ë’¾à^ÍC3?ˆêzkòÏGS¼Þšk{¡R}P±¸Ó» +ÃÝsî×´ŠÏ€S4N£ÆevZí]êÔ®`¼6l(.ÑáK@!ð”$\ƒ©½6–Ë¢ŽÁvZ<ÒûAŸ*äœ!%› +v±Ý$N¦»bTë%•jÈ£¿\£CæÖ?åÕ7¾AŽ«úöŠ‰ëá«¿¨µQf²Ÿ¥Øtxê:„;ŽwÆëGÝ *fZjEIyW´àØqÈÎÜ;\7¼>Ñ]ƒ¨tâú±(mÛ”û”V‚žò`tÕ®4`è±+?j ™“ k•îÈ6q`³²ÖÀk¡Â=OÒ6„{m†öìÌ¿›XPüÎþ•Þ©ÆJ²‹„' pb6m¾eŽç±ÛFqÎõØ!³›Xˆz³'Œ¦ž;–àv‚mdS†l—0ÀÄ¿B7ÓO½ºW0 “¡Õ7!$Ԅ⣚çhŠ*Þý©½e—î ^dœ–ð:8<²„šP“©nóÏÔõ8ê(»9òþ¨j#Fí_Rµ‘hÆþúÿ({“$Ir%ml杻 °Ä0˜8×$"ÅZd-È +¯O×oPÀ3£ÿ~-µÈ‚†¹…ÎCˆyô¯øÆøp®ÍS•VsÑ\µYE8WÓøÂôà +wR(‡—Í5üÚŒ<ÕÚæXiQ†¶¥çy#Ü–Üahq êRÆU5´ùÞØGØœ‰)¸¨²„?#†z-ÝÏ»(*ˆÐi—¢‚ ¡Ã¶Óä\@US~tOoæ>á{oñ,âÂtÄÆ-²×ü‹÷`IêŒ !ó½Ì¯£¦ÇÅÃåÜŠŠ¦|BìüöÈsÁ8ã!oº•@~ ƒ‚<)Å\ZgìÕ7 Zƒ&$é€â3ðFØra¿ù c Éà ‘^‰"›¹vûû€àÌœ[^J‚> ,ð„\ŠßT´·…ª»@±×?h3è—‚€†W‡*ÚUÀA°r1Ã$ÌSS ‘DÙ#:é;X|ŠOªª´Î‰Gµ‚f ¤¸E#êP塇 ›.“‚Ðèrp1&¥¨Äø³TQÆþL ²É«ÓKU¯äRÛð´!†SPˆCÝr ¶¦|ˆ5KTq?°ü¬NŽþˆ7>Žî€%âÏ°[gAû›+eí(pÎ_0žNÜý_">4m—T“_q([B¯ + †„>yŠÌÑnE¤%éXºÂû‰Ky¤µS&Ñøêíòþ +©ÍL¶WúsŒk’› +¡m¦Î”ê]ke©þ“¡X—zd#ÕÀ÷<“ŸïÕÃuó<ÑÚL¥T€þÆ’ŒP†òb2)+ \â +Åja‰ÌÓKM²ˆ^…2DŒ—LѺiMÑô]õ.F<ô¢ð†=¾Œ:ì Íb™ðÆ´9à«$_uhR"oÐÖ'Œ•‰…Â:¬f&— +Ñà<Ž±Ï:,Ô‚˜˜¤|Q²ÂÆeWòœ©C:ùݹ¿ó†à…°ðyŽŸK¸¨ÔÀÛ¶bI臵|YÃ%ÄÛ}V¬cÊthž¯f‘Ó_Aš¬Á:u½è> pR2ª6fE["ÓV”lªÌ“;Ý?S˜Óñꛤ怨b}Nñ•Á"Ǭ¿K­Uo¾îÏÚB䆰Få±C¿ xÿüp ,@³ÖjÏÒ’qa“Cã—Râ­§ŠÇ1E…§Xê"u½·0• +Fö…{Ž'Ê_Rt×¥e¤¢»²Î KNŠ/ÂâJ!Ÿd;/ý‰¼;ð)UŠjÅA®Èáæ+ÜžB;tWô[Ïþ  2‘êµ»pEršØÜMòÈƦ’ón°˜ºwk³ýÀ²ñùµ.ó§]q®ÓnáKiyÅÎ`W·ÐFwº>»VÃÌ?ÝÖyê¶~J©è,^_²i%9ë×ëËw@\°ÑÆ`r÷~a±rçx%è—A<’Σ‚·¶³Þ2už¯ÃÜ[yJ0µ}lù‡²›Kþ Ü#š„†Æã2 yj­%úH­Ž»D¥ÏÄ4ÙÌ9+Ý¿„èûñ†fíO4­Û³h›µt I]údzAM¸d×é%Ñ‘ÝCŠX|Sð@‘¢Õ,™ôÃ.piý\\ÍŽ8œY…±VnäUòë2V·•¨/­©£ø»UÈá™UYø=ó´¢öŠ?|ø}ÿk6e÷œnõá÷…H×­jrØï»]é÷Íf©ÑwV¾¢ãVËóë'îƒ-:½läÖ}°çW²zº~ãnÞ}»~·Ù;]¿ŸíúŸÛõË[{x~-íÚ­8 +þ+Hq;¶4 È úgm­0Ë‚ô¥XÇ-’ªt&´fÁ 7ôï--µ»/E1ƒK•˜ Ûšä +Í ˜¦jññ¶°„hÂ\Õ힉`, sÉ€pìqô¯ÿÞZÍj…î$c£B«ý¯ÌÅøæ1Àã1y°*{uuÝüÊ\½wI$ê3P|u@tn +Á|—ú${øÑ®;§X½«³ø°ÃgÓ´²-ÞqI»Ä?¢”8àÇU„ÖyÓŒ—]7Ôƒ‚Fj1º…¯ÊÝ¿Ø [L©Lh»Ð\S±€×Œ–”㦳"öòá–D-EÝ&äf€aœÐEÕ§xB*XÜ'ž¦Kéx‰0ö)zùJ³*å/ý‚‹SW«‹Èy1εœS#òTW½žQD?INÚ¡‹”T;LšÅ!u"—¾‰«§„MÉqÆãCPš|ü½±¦TØoŠ0S/d ôÕØ 9 ¡7_A;E>“«¡i fxÍ—~#aQõ«±µ5ƦB5ìÂà Eˆ™‡›)D„ ñÿóÃí½òÛƒšãA”MŒÅÜnŒü%%›^‘æ#ŠÈé«zr‘íZ›D°dÉ5Eëúóác¤g^S$¯%¦w5àÊÅëÆdá!ÇÞMûŠÆë Êm™ƒ†šH4UF£½0ÌÄXr_ÈšÇÐGoÈ­½:êÀº‡s$¯òÄ1Ï¡²[¨‰Ë)ÂpgîõuJ0Ê™Ê'–ÄCHÿ/žDå \‹!^(JÆ\Ö7Píb÷’eÄ≦.…t=\+©?q” a1Ô™¦ -r©l/t…»9¯Ò6ÿˆ¥­¶™wƒ—w?qKq!…êþ_Õõ”‘,æ)4Ð!¢!Ê16ÙÆÎ\3¥u$¤ÄG.>  ʘ±à&j=–î5IÒä̽¤¥¥q^²Ò…^KŒ‚vsíÕ¤„0§Ï°ñTɼÔ9>ðYìK1wð…/%nÑ4GŠ&õû’•K¤ª©¤úÙÇM†úèqÍÖ…rÃZX,á‘ñnıi&&غ¦EâÔµ˜6Â)IþŒ•$®ÁKMB Œf³‰EsŸ‹9¨‰'âëÊ-4ßæ÷.T‘xÄóF“[W…ÉAî¼ó»»m7þÄxRŠÇz1$Ås8ž@Ço¼¬–]‡§9W§Ècœ¹x9ª†›{üÌÌD=v‹Œ ¦à7>äàœ†ZF…Š/‘kLÛ¾‰M`SFz©ª]Úût,¿CªäíÆc|8Qt¸D z%™Ù¦WjòR˜º ÂQªRWZäJÝÁiø#RÀ_Rv«Õœ;éìfZŸŠ¸&iYdV¯*Y^ôËö#]êVQH—M¦dØÎòE˜W'VÜÚcU’å +¶¾Ž›ö®EöD +¿ªªk1ëe: Ÿ˜ø@„ãÙß÷çXBÔYË€šäB¬Ïj=¬åMéiÎêÔ÷¹[-·â¡Ô#Õ€$xn!õE'ÀÜ2§´\»쀡Jñ%QQÌ[óê9Ð]òõþ¦ »aíGˆM×à1L®^TÞ® ßYÔ¡:ÜCÇpw\6äq‡e5†6·q¾8_%ÛÁKý,·ÅÄ +ïÇn§™%bmnvPuí +ÿP-·Ñ@´•ä\X<''áLÅh/IÌS"”WÑÓà±ná;Êòã§ü‰w†ÝÙ÷×ú;Ly3ë^ºvmÑ<œ ºþEÔ +Iªîë]Øꃅ£ù +µÑ +ˆ0R²5øW÷-¤¢Jö¢È˜ÑI¸¬ö‹=%cM¢t[vë·i¡‘Íé5Fƾñ5Çš§œjð}Rl¢ŽM·ÖëG袕Ÿ²ZÚ¹ÚØ¿ŸOp¬ÿ¥~9&²ú¸Üº!]ÍUcÔ4Ñß4‘5D%>Œæ~NiDÏo·jüÄyÇC;Ï*ÕíÊMrMª’/a­¾" FU%ZÉ=r1¹¢´oØödíÒ [É.£p+4e«k%bœÖ»ºßöŲʘʭÛÓ"—Êe”%€*±Nð¹Ü«ä„‘[Pö®vKG- ºcYŽQ< +ÊB/óZ¶hžéòØ…hÙ­T% +OYH* +‘`ó(EKÚw)MÀˆÔºÊƒö­R‹UiIƶ +q"€mŒ°o¡)+™ö•õœÙ‡ (–*’ÒÝ¥¹‰”íÐöÝ9aáþpöÕ•>Ùšé(a#X¤\}Ñ<¦ÜÃÀXy—¡/¾Êr‡èÌŸ¨“zè£_ Þ¼ëéÄ6{ö/T`d?”¢PýÀàÅñ%-Ùm/ËÈR›2׋Ëâí˜ RíJ ¬æȇI;ûëã>»Åa½WB]±kC…sσÿ"£Éœâ0d#l9Æù UwÅl¢7‚¼Ðe/rþì<~>oÈ é¿Ï‰õdY>\&P`AdQ½™ãði£EöF£–tfˆéÉÌ)ºoî¬ËŠ¾…lt¾$n=ü¹bjŸU¯xԳ¢Iy,íဨk§ +ØÊRLJ¼ÚeaGòæªJx— î´û¤€½²L£^_wÎu0×“Ï J¨êíÇ·5lµ}jTª~oÄ™’mS$>A!¡¯½Ýq*Ê*ÒÄõ/ÈS\Þï’yÁ5!‡ìg*b„ÿˆ{õ|J ±ß]¶ –ÑÜÖÝèSj×a6&­²˜A‰¥<‡[)’˜ì».w?½yÀ,iJ¹ŸÇX¶Š²m¦lÉN½s+Fö‚µ¶<ÿrHÍ b/8¨¤9jÙÅÊ£6¦HyÔÝhÒ +]LÔ6æâr”Ü¿f¢;2@LÔ +'Š†(|{Z% ZÞérAÜtߨ)·Ý7»)±‰*ÄmL¨êdŒJs6´'ý_».UE(Q|þ›ßHë˜~’#”’L~¦:’Rtò¸i·„…’¿ÊV“†pB¾,Ú2¿mèb|YíÀñÿR¼¸¯²§RwHáoàñá²C>–§ +)•^ò†07#•˜Oz×ÕO ³»¬mH¡Êdr¸Ú`"“uÅ«÷ ëÔß퟿¢àÌ%‘ÔŸPöœÄ-Öà9î±8>!Ò 2¹_gp~$Ñä<9wL³³"â^H·Å0WêrrÊ<^Ñ$)ïT·¹S˜w›Óü<†’÷ Ò•ë€@ÌòUÕÁëiEÚg$Y.g^•Ö3âI„2—¿ŸXÖ¨YoÒ;¸¿â=ö<îäàœæ»æß}@"9À¯oïyLÅnŠœGQ&‡çYX4v/%©X.VT,i¿¢ùÚäGxLžFO¿5§ùy$?ÓßÈpôFXÜ™bèaŸ2øު˹mg’zº¢;‚F‘ìâ°çÊŸˆÇewPÔ• +9.Êd ¥©d¡£Ü]Õ\¶Ñ¾Ð‡j0õ%Í+:·”PuöË /[gágc0}š;¥á„ì€_8ró­ß“bMW).›Qû³µ ;ú“6L/ð‘ù°jÏžäÈ AO¢¿ì)ýQ± ‡OvGó+Vª;>“®Žš±ïz‚î±.OßMªõÞÍÄåxŠ4¤-15m[Vx¶åZG°vŸIú!ôx*ñÕ‡¡!ñRI€ +qßÝì“Ç;äÁyh8‰•ÜÒ–Ö~ç?`E–|âaAž77X“p1Õ'õvð}$=Ñ+A‡˜ç*j1ÕfTʺ…à!rê—°Â?Žù‘C^—ãùK¥ë ±ÇÄ×LÆ‹3\%Ç\Ú£â}àÄ83½4~ÛµÙƒÎxˆ“‘Lȇ™;å»y`ÑíV;G˜6óþ?êA Ñ›8Ej߸Sm â´h¤Úª![ý‹´®û¼’ŠeþèOHjþø‘Üòç„".²…6RYêóß/êV9‚‰0G²pÆv¿‡94> ™Qk6¹‘N%ª4©Ea&VW%L|€`¨‚Ó*]è€Ö0†Ô£}¥f“Qd +*Ú¥ÚáUÆËKV·zñìõ>¸xÅâ…£ð¥Åâù‹n´wc\>(V2c¯ÞÐW”Èí¦°‚1è$ºyR( +¶ °&_’d«š]Dzàd@KG¯®ýH“Ñ +r³–IØU™åìüU™·€ÂWÀ>YC?¤÷§%vä8b« +éI}ܺûbii0³A|ÁU‹y4þÀ›0àdŠ™Á”óЧ—3vës•¿y '—"PÕj;¿»\\?öH6J¸àbO»êÕtΔqˆð¤deæÊ•¯‹i…Ì…x„ ð±ÀÈ9‰ °Ö©x|¢!·Œû;h×`ZÐ+¯³¾r 'Ë8ÐéQ i¡¦W]øRÙþú8±Ð`מIܘì§r©^Ubg’ä!ik|ÿˆLº) +º(gì@2A*›Ê ²Ê†0‘¨—uc ¹å]LŸØ¼¡ QàûÄ°>¾THÙzòÊ(ìíRuÚ|bä4À#h²kZ•õÞ#òR·ìRP/dï sãá ·'c»u€ýC+}q,õtŽªC¯J%ûÆn SJ<âÝ»ØÓýTowâŒì‚Ê—.å˜ÐB€õ•‰z}*‚s=ºêË“×Ã<Ap*“4Ùö—ü`{¯ú.¥¸D‹Sƒ}%‰ž;f†Óò@Ë"¾túCPU¼ury‹núA”Š^‚¯€Tf|\"SMÅDJáa‘#âK¡GànÆW¥‹8X«£ò¹ÍO¸C‡ßtÛѧè›Ð¢ç1Çš—Ïh/xEœ.ÀÛ<Px ¥½:ÑG;êî‘Wã‚Jr=")‹M9ri+S t‹"Ü¥ +„yÒÀ…W~F&m œü–’6a‡ƒ_”&1b?T5ü‚×DñÊ,ÒÉ„©]xž¤Küâq$íµòøØ{ý¢÷ •éɼtòv1ú8S1&U ùî“*Â*éÉ Î pˆã!Jì ­‹šgLƒaì(ÜÇ*;/{öX+…Š[¬ŒV€z‹øSû¾<ÎX+%9ô’@­ýÀC²82 +¯lÙBÆFXÿî<Äò( ãÀPT³Õ’¬ŽÚ,/½2ö@õ“!imÚ«Aµ—2LÇ<µ"Üy2 ­P]ø€@¥ˆSéÚ3ž+³bI“¨>£û0e{¸ª-X*|Jž>úÃu.U @üáZIÆ- (ný}‰ÃïDgä°¯ò“i?JWªÏN_ÒâQ‡ÐéKäüb?Æ'Öû ÀûÆì˜=¶0`ˆûŸnñ߈4óÇy}B†ú¶+`¼O9òú–s2ÂóqófcEunc:Š‰KbAv€øb2Ô[Y»hŽZ‡L7EyoPØ®Z£ó-]À0)—¶_ +ˆ—d¹@ "Ù½®ÛU9tº(2V‹~ÛVPn¹¯--Þ#6À½á +YÚÅ„ Í,O‚á =’Ý© T˜\,ÃÅÑl)ñ«åã Óþ¹lþ•˜#á ¨¯ „«/š”¥¿4ÛQ4-ÅD½Ÿx%›ÿÅ7ˆTEcT)#U½—›ÄŠrv1èèÿ5f»63)ï`7°å?_’ÌHi g°Ã—Z~¢(bÕ\ï’»xêLMo6„eOñ +²ëÛפ•ËÓ"ÎO±•.a|æ<“¡ï¥wUeD9V¸+ùŠ"¯ý×½³ÈÀØ™&|÷^ÍDð‘™*‹G®ßº" +Ò{_íÁR«¢Bc\æ1öðIš™€1énò)Ž†z’.ë£$NM?ªç #MØï:º `Q5” ‘C>NÁw:ËüY,£3£dXxEYѤ\Q)Wx>Ѥ¯ÞêñД¦s|¥I?¿ÿ’Õ˜túV~Kºìp=S‹È-'k)çÞrŽ»ºµòí¼–OCŠR>AƒÅ-×rÌ¥Y¦õC¬’é1?*•§Ü))ß.<"²ÿè”N6à äOÖð7:*Æ>}?EŒ•Ä¨ê8ea»?—DH†‚”ä +;Lù¦%E줖D{°“Z|/„äµäÅ7‰€ä~r“j‰-¹IU2s“ªŠKæ&Sª¿‹›ÔKeøÍMê•D‚Üd%17YÛ’0ÅlJ$n²æ&+ɹɢ'Áܤ*CrsF¿Ü$–¦*ÕàhÉÐNnz×'nUô%7©¶É%7‰Úüc$7©–úÄMjM¦çöÐ)*&?©U¹øIUÃ=ó“@qRazeǹd'1qñ4ÐáZ’뉡T–iËTý× …¡ÄœUÍøk}£86]‚øIŽ“Ÿ•ÑVõó“øÈõÁOª‚¨ÌOo~ ú~RU˜ÖüÄýBÌO"€;e™âÍš|)ùIœb?øI-i¦Cq£3”8´û`(èñÁOЂ¢øIŒõNö÷89Ê!K À}°”Äd)µ¦Ž–RÓZ –RÓæ—,%ÐS†Ñ…¿GI"“´¾J2C©*ÁivQ?ôDØÌ}AæW2Dœçû_ú +|b&J§Nf¢„냙¬¤Ú +Ÿ¨×Èâí—÷€uįÜD14/‘ +WŒB8¸ÄËHöu0’¸>3l”qðâ/ñý€îPó‘Mí»üAüÝ}ãƒüM˽ôç  ÿòÿ¤w2_DÉB6lUÓôU#9ˆÜ±ºÑcAítÄgØ?µ¿¢ni^{PŒ$c‡§KÀj¬PÕn=‚»ŸÏ\¥Î +r¼7›G•Ù·È°2<½HoAÌãÎãÎ]÷ë`ämiâ)4 T‹Åc^¢çò(f }l ~—ÏÄ,Àûµ™ÄMïz>q3ò(Æ/€\"½*[f_6VÖ]¹„ô…PÕG½tÃ>\…©-È#6$³F}ÒÒ†°E &©à3e5@î€RôR·hÅ“æ<¹Tb³(‚ôË  +ÖØtÕC³¶ôÈFaüUfDäÇU~‹×=­ýf—]KTŸ¿K5}$ótÙs`uÀ#Ë ¬¡híqX¶âWäD?Àر|ƒO0=ÑUårwÒÛÖæÓRTÈéÀ%™˜Ñ©£m¶¬@Äfß\½¤33èd21ß¼8S6U”lÊ™î`Y‘'ȯÈ3L-Ê:^zëïïÞéoª¦+vjæëz~3™þ±`fNðŽ>v:Ò· +P‰¦ ²á@Ÿs®E;œ5¼ÿ^‡ª:6lj#¨ÁzÕ‹KþO›LuO–KpÕO.³TLKT’ +hº%*Àf·*ϾüžåWú> õÃ%ë¬óë$Þ“m4ò‰‘”yê2ˤ’Ö¿:r+.Ñ×ÛqŒL‹Æé4}íÓ:ý¨œéß S¿y¥ÿ ¨Õ¤2ž'd’··Ê^0ö>7%mïskÊù1m‡Ç«õDb‘îÖS°æ÷¶ü>ŒUm]¨œê—àzEdªü¬wãxZ°&]ØT­ŒÔží²šÐžt²KéjÊ%·Ü”UŒ}­¿¾V— ÆÊ?–‚MJBPÝËÖ¿’Kâäf%j5™ ö–ÛØ‚$ª”ð> K›ã&·Ä‚¶y¼[Ä7¡/¤‚O|n"ð(ƺtꌢ× +êü:‘,Ÿãs«˜Š`7€1mC.Fz4²Cš¼íÕ! + ³äáÑé`›/Fþ íeÛf{”^šMi)D¶Íê f6U–0ÍÊ#ÚÛçŒiÏ¥®ŠÂÊñÆCÔÁ:Æ"N ‘iJ8.¶Ä¹ä‘³ÆoЯ¡±è™Lq$¿(ä…J@0´Þ]Õ¸9ÒF[gL$ùÆëã*;ľ õ­<ÐÉ°‹ßŽüg4`°ŽíÓRòÀ¬ôé'äÊL†ßF”’åðóã?@|?ê:²aÈ??L%âGKúí¡ÒËö’¨iK*<GÛʉhd?”“®®ȵ³ +….CîDt×"¡2~M‹×dõš,ÚÝè­šÔª@]!ï±tYBöEeWw)òhH_$OÀzI)¥s]Iw +ü-;^A¿ÖG ùõ¬dØeäAú³ º’# ¬ú¡îCgäaSæïÍ,Ñwª¸t@TÀ¯FX?f©/RR5£µœ‘|!—²JFt\j5›_Ã3-²Ó©ˆ~MFÄxÕšÑ SUØ}§áZ廞*âÖ‡%ƒM¯)Y:`µÊ/1è÷Œšé•ˆÂÈêºc‚à¸Q³‚)õl¥×ô~‹Ø¢áñ¹©œþæF]|GywÀ_þBmµèBssö²¥&éÕîYc½:’>î-Š•CL#Þý××;ÉHÖSÿNéÎÚkªÕj‰½»ú§$×·Sÿ¾²MRwì?à dž֮¢ Ü™—^ò8@æu8ù£@ &ý*‰$Œ':“BTBkÜôiÁ&Ò·®#Y+“`^e® wmÑ\åÈ~ù“)uÝOнÔ×ÙùÇW”aó¸ójqš{,˜I +Žü9^›ãñÙÝúöa!(ç9Ug'—2mïÈÅJs’Âñ +ï×þHUˆ_Q‡doxUÆÆy$I +õq@íØácŠª#7¢îøã<&É6}û]‚ÞÓ†gºáåï'–úŸ¾ßpÓþŠ÷Øóð±xš{¬PÂ}Ls«ó7+æGn›fr7Ú6îyæ½õRöÕîû&·ç8¦ý +ßìý“§±7œÓü<’Ÿéo¤EY2{˜<›E»%"ñ8øyWIªHU‘q`S°ŸÉG,ì@éê¡ñ7‚ÉÙÌO¨6(²°_ 6·pTÜ[‘E1 +üb¥Öö\çxZÉ$„<€i;ôƒ[63¨ÖâÃÜÕCI +õŒPñJŽ“{vaãÚmyÖV*lŽWÐû=”ñy"<¹çcuÿ~”áíªdñ©šÇäâ35¢=Ò7ò¹ µ<ÒÚ‡7!ò+¤‘`;1 H®P2…e<µS%Åú0z¯;ÐÎtßû—Úòæáí3­ÓÌ›é‘5ê+ï58¹:ÓØ©‰âô°qaC”þ@çÍÒæ©¢¨uÏo8jäæ#–Õ(H*Y9ð±ø'•÷ÄùSEãg*ÃËãâ0ç#B$i)x6Æã¸5—%«¡šù~à¡u)^éAµÇP"Ài)Mâ¤Sš|i:¨Çoxõ3®IÒ,Ž¤Ž­}$$5¥p$ “:¶–ãýIéÖ?CL(á,êjÙg1Îx‚Ðï-‘j‚³Û–ªÎÈU œès£ö%q?9Ìö7ñdh5¿áøõ•ÍoâÝüDù}ãêî¯dgt¾Á;ïâ¾7xcÌŠ}o8üʶ7«ëM¼Š]K‚î,U³åÍŠ†£o¬VK›5!ëz´´>õ»Aˆùåv7‘¤¯‰'Bè[SÇ‘îÐÿÙ³ßÍ•ÍzºBîs‰0’dˆØø¨Šï~7—y…ûÝ€ý‹BHŒ€|ušY ð§0BÀ y{îw“€ly¹ûnw£±ºÝÄÖâæ3):Ôë&ðè"¦K]ô +i:±¯k~íN7ÑÊd +9 EÃs«ÓRÚ+;Ý A›ƒZ:<îtƒt†’n4ÄÓ%ÓÊ+;Ý\¼\îts]ºìîtù8<9PÚ‹ýk5Zèœ5û•Úܬå&:Aht÷²ËÍ¢û@›Sêq[84‡®‡>Þ7¸‰Ãp«§0åÅQ­—»Ûìƒ;Žõû‡ƒÞ­m„ +»µÍÆŠT`·¶9Œ@ê”nÊÅ,‘æ”(cåí„JV?_ÙÛÍ?ÐEæÚhŸM=`¦¿Ü5¦ä4XÙ\½mbZú…{ÛÄÛo¦ët¡‚x5^ËÑLjFjmS&¯dX,rCÝÙ&hÌäú´ý] +{nÑI…Å¢­Í×KuµY4ˆª©M¼‹ÈËž61Õ-m‚LðÔâEã·~6û×ýlq?›ØÙÞ¾²ŸÍ•ì÷º¾ðžçÎ~6»('\28°¦¢kÔÎ&Ðrò9pæ÷ùån6`N{jÜ6IaP ØÌ—;ÙpćWTŽôŸÙï%^¶HãÖ¯yd`a#'aõ§‰‹)œ„éz¥h³€©Dýkâp"à pú‰ìcXÂ$Ö?ZÃøÊö5Èô|ºò²œ» dœ˜2¥(—Ç:3™ªˆå,½ fŠã(ÏÃþŽ6Ø£çh`³– +c8—†‹Ë‰ ê^³Ò8¥æ5‹ÑEî]»›Îrq+Ý¢j]ˆòDK[u®¢ðsÁ +%,ðùp¡, +±o.œÚÖhøÒãLøcX—œëêYs [Ö¬I +k^zæ—SžbòJ›cÚ(¯òÅDXv«Y;§æb9‹½jb燋b‘C÷ï¤pÎIR}dBÎÞ5‹¢çFfõ©¡ó¥65ç‚ѤW‡œ¬L2”œGK‹ ×L€Ý¡F‚á‹j4Ú +jbÿÜDIÛIáÐéb‘4¯fÓ6§YÎZd¶X +slMsü5YâÇe}¹/ +DN÷˜‰Píøš2Ž#Q8–©~1¡ÄIJ]ÌC¡“Ù£%‰…†îH³2”’ +i-âê5BMߣ•=;Ü­&öŸ.âîEø2œÕ$3£MàËÍÔ}ôhª!²Æ–PÌëXŒ%pÇZ¶« Í¢;È=hbÈDàÍJƒ%ÐRÇšÿ[ +Uÿ™ƒ„J?`X¨›Ï¼’¤²ùÌZGÊ"ª‚O0ÝÌ‚ÜÇøë„LeÁ +5c#õž ÚZÜiÎPß/užy}mñAåPC­gò µž‘ žg xg« Tp€l¬³•Y¿L÷¢<[²ÛŠ"jÅu³õ É?±q™Ÿ,$¥Ú+¥õž È"n”–”_ :m³ îbǒȾÓ¿Ô{†sȤÓVÄ•H:ÔZÉéåc¼ÛýÒ´ç–Ÿ«ò†à”"Å_pÎ#z,ok’㦔Ô…o6Û[<0®¬¥–G,ʼvr:ûά•ù|7å¬G½[S¾–pj:³–’3%ž­Ì»û¼Qê8³ªûr?™¸±H«É¡û$$ÀÉ‚!‰UUHZ_z^’‚{ÍÍR‚#MÓØhfÑ×®63!!"¢ËôcSz¶™¶Îç/*Ú¤·4Ô-f^ç•$•?ß&Ì0$Ø”ï‡ .e2t—yYëßa1Þx÷–‘ààÖ2)ôMo^ì—ðõ®[p`è ˆJã@%«MÊ“+jõ»DL›TÞ¤ +R àØ”Úɦ%?Ê$qßýd#¬~j'3DðÕKFCH0ÅB§ò ØÿdÛà{Úmk +†c¦¹ÆUl¯A•r;9= S{_ñSw‰º:=Ô’óÍž²ðtÕ³AÙãNaݹ\L5Ê?3’P†VíÛrÃÚ¿õ•BOI™s¨†ËËíibp|È~Ã* ’ã }3 +Gd65VȱV\ÒÈóúbÙ˜±™GÙ¶ Q›¢žÂ²ã ŒK/ü³üÓ–#Ð×Ö͉ §)‰V"þ¿_êŒ`L/Œ7•1‡õnʶæàhßDêE{gÅkÈÌÎ +:{J¶èàâóQ_GŠæRO ˜Õ7CsW Yu°›jz©XCîÓ²ÃzÕÇÊM“e‡Å_ÉfÝg©3扶×ÎS’uÇão4®B«IùêrZ‰óOqk†EUæqÇ’kÊÆÃ!É{ËlºpŽKÆdVZyỤ̀ĵmçAb›¸ÁÊĺ<ÕCÿþ þ~eÃ!ÄYÔÓ^b*¼ '9j‘œmÑÓÂIâÃbþXŒ;3\Š ñ7 ]ª«Ÿ°$r3[PåLâ+ÍÙ—º3›‰ГõO1£d´s ¥RøQù #"Ǫ¢?`rºŠMAàHµ¤”ÕQEì8TuC˜UÐÌ0ä‰ÉÔ².qAÁª0XAÀ`('±T¥bw™ ꦽ+¢ùí¯ƒ…ùóC) €Ê£a›èW0Ñ!ãzª¯sø¡ª­Aøº‚ Ã#xÙÖ>ÕÕW9m1þý®ò0)©Ó®¯Ø§È ¡*"¯¦ WÔ²ÃÓî°¤ñ)eиÏÞË#SÊÅàò㎴¬?:å–QĘNÕ½ ,… +†3HÚëËæ6Ü$ÐÔ©V +b°0¬•i3‚3N§äWê󸜗7í013IR§§EÒFV2[Y…·úEUnr²´_¹íÃ’S9¹ìÙh[Š.9„^rÃÅZÆ!4W=.¡˜}^.Pˆ*Ù¬‘*:¸úò¥ðß©YïñCÍq?ÿ|¼H¶„ÓeÜ”è+ŠqáuÌ +¹’û /Ý1vùÿ +éj’ÞF·«ÖÖ•žõ˜ó»"*6½P/©òW¶wJû·ù×b¶€ÍwêsÙq‰þÜü{jcm13Óꘫ lo*™“‡â"_þ^FÐr+pÌמÈR‹¡Ð49·"ʲØ/¯æ¾v¹v +áJA•B#¹Ii«“ÀŸ$Lí[(¶fl¨(æ€qý ðð–(„Í`…!…‘[Ê|EŽ‘Ma„<áÒ~ý:±«?GÄÌš˜ )šeèò’mš¤ƒe>'í“©í¨ + +Œà=‘]4vºñž±žÈ¢”»Ô+jÙ3a¹,¯)¼íè³BoSãëQzheœS@¸;l­¶HO'oñZ:J@ðàW Syæ1ìEdŒðcɽ~ƬÈ]¨ÛBCl«5]M2?&$[bGŽX9;^©WJ—¹Ã?ë Ïý’Á¦ù+Aö‹b—ÔI*½1ŽX¤¸2'Ȳsö“‰? ?‚+š×™Ct +iÈÃ:ÿžFMèûÁI/oŸÏœJSìžÜ¥Y†LÔ-#Æ!¿´ 4®ƒËÅû™"Hc·;½ïùQ‡F´˜ÿy_ÖÓ*Kžl5.² +{úß‘'HçXG&_µ‰Ö©ã• +ŽµJµìºTM'”Ð$w1Ód~G«Ýjæ¿V½Õ$˜¦PQ +BBk|–Õ­5:Oi+Õf`üS$Öé°tcŠmqt£™ã‰!ÉöFæ³ÍlÐfdQÌF3’é³ÑÌÖ”²ÑL°E!½Þ¢¯ÍÀlç60K~pæÔFN¿ù:Ç.÷_¬ûL…ª“µß]fs …hw³l¿èºÐFÛt Šl%V±ÃŒœ¦ŒÜ¡àÆè žÑ^a™ô +enÎCê.¦#“Ùƒêï£"-4ã8SkÚ50´-¼)N-:7f790+U!W¶"*ùTžR~PG™ J\~$1Å­pmêã=·¬9+{o÷âsÅTwÚ_§åëËdL’öYh»ÐG&%]öˆÁ럩ž1x!À£G•Â!ì·î h³y}e ™àÐ’@-dB|«[øº³ ²×(Ð)SÀ…4I5b¢.j!cã·[ÈØ8~\TØÓµ­-Q.[‘?‚úÊÇ)ÿxðG™ ¶Ø]^Ò¢“V?ÈòJÐzìÅÕ¯þÜo'àŸ¦³{Çló‘ZÇ$FŒ4±ªsÌÿÔ8†¾Ý×—ûÆùÔ6fû†Ù5Æ®cv Tª KwhªÌ§£0¤Õg5„‰‹ÒÛf*ít=>/ú ©]L(ÙÂÆb~a‹VðOêKÍb<üfÂá%ß|ýh¸Ê¢¤nÒËÑ)†G9ü3ZÄôv†ùc¼1D‘–?@êGËGh2O²Ow&Úgældw¾¨Š[ந„}^·‘Æ=·&ÉNØÌd2ª„ +ÇfÞYUÜ)!"ã+;ÅØ ÄN1rò¼ô )¬ÈÚL}˜b«ÇÓ-_$1‡±²AàÁÔ#fPµˆ‘š¢1ÖxØ Æ‹[hÈí ¡âê³í jîU¾Ü&YßÃ"•›÷©3Ìñ@™)±/Ì!°©+Œ¬&j +ósürS˜í’Wš¢$®ÝÆ^5„‘”Ïv0/ogWiZ̧íýޔ⃒œ`N›d¿ÌŒvŽ60¼risÍ\/·A1ælÇ‚ +Åóë胀1¿–Â]˶ +Šòil"–c*5ï>*KñàŸÏ•™; +O;RB®Y{¦„„d׎”-ûeJl§GJˆm«Î£ØÖW§ZOØAÍYgk_Q>GÎC) 9Í=v†ŒSBZ–¯¸”tè\G~Ç’DU˜É¶¹’+“%½VB2#d¿¡‰êìO ¤;'ÑSÚøHÉuì„Å…ì3²xÄ#ZÎä7·ECszYúÒœaÒè¹( +÷é_RºwS–×ùþÛJ=¿Û‘ç¼ +€ð¿ò烵z†DË2èïqÚ‡bÚãÙ,âåíbõÇÏïÇ~=Îü:wö¥Ùý¶õ?žÆ‘Lw¶3;'гgrN e}¡ÕLiqrNôèm'çˆdšÆß;9'ŸpðDUfgçÄW¸Ô•ÇXëJE½Ïñ•I·ÊÍYª•¬Ô(¿+3s¶n¯ÄœEg¿Òrò † ÌÉF””³v¢ T)9R¾‘Ùëösj{|LþH‘8Ž…ÉWÎÆÑyf2ÎZ‹q.ŽÝkJÅYΑM7¥[U1ÆÁ'qd˜aÎë°Ì( +džeá˜SéÄ·–Ã^N]±’¨ œí¹QdŒšÉ5Ž–r® +7ÇíH ‚ƒÁù7’Ö2ÿ47¿ KÁµœ}CVÀT×™ÜbyN•8 +±ÊÙ7ˆ‰;ù†ã•«¦àùÌ}s©EhÞ‹šIDƒÙ`ùÄ£„ÞxiÐ{'àˆ3íò6âeS Òë¿À®€#ufçßl…Ç€T‰˜kóÛøujZÙ˜åOHÊÚÙ=åð¹ÿ>™¿_ â¾o;Êÿûu"6ê¡:†åb¼¿^¸o@˜u^oñ +Q`Ï +wZ ç=b"‡x>ÂmŽ¿‡Ã7é|rŒáÖyØõ€7Ê>reðµ7~xû0œz<25B/~á‘÷²ãC=†^1¼à }Ø]€7{~XlË»ÞWé¹óm1ÄóAróïÈÖÁÛß‚ïÆÓ1lA +ŸÕòâF>]›;‘T’ð• >@¬*„5ŸÆÑWõó{)-3õ «Ž”úo×A8}X8èЊ_Ÿ÷}@‰óVð^2C¹—Þá'Š¯ï,`xèyÀqSãç¤0ÀÙŽ¼}xȧ˜b¤Š¹˜À„ŽKÑÐË£÷%ÿÞ¹èJ'‰(pÈÂØ zõÞÚ03’‚ãÆÜ5é=ªµƒÌhÇ*ú‡h.Ã0Ρäl‚üzÌQRœ¡Ôpˆµ\,J›âÚ¼D/y!^º€Á_fáñ}›$[ßW@ÈEA}ߣyP×߈G˜?èÉ߯…îÁóŸ•uö8ôÀ’2@xV‘@ATþþZ )_ \b…z%šr;×%ñ’ÑÂséçÅd xàÉ3‘{3—¸Û…Âäs‘Ôy#à¤ê¸Ædw0lZ< öUgÒ|@‚€ÅøÈî&3Ãx}B(ªÎ¥;!‘¼ÑlR´Æ2ÙÈ +§L$,¬™¿¿1GÞÁe^Ú‡‹” (d}ï÷Ήh!\°7²ÍÉBMØFÈs!÷ÛH»P`”uÛ\Å8çBjr¾÷nÍÅm±Y—¦ÓqÆS$c ÂÄvíQg¿Ñ +˜@2Í›t†*e u€,Æ +Š\ñ÷Ö9œQ1V +|º…Ñ“ÆæÅG‹Ë/½IDk ©.wìÖ’£nÐ@ŸQÌ †«ù7o\¶_ñ„U£ppë÷¯G%²9ôüœÀÜ@z”Èœ 8ŽQ¨s!ÚC¬¼S~È(=°ËãºÈbz×–~),9—‹¨§…ø¾±˜VÁ$ï(¢ý ⹊&áí_ GÀ&ðïzg¦˜OJúßhÇ›štÄâm'aŒFzô6Å°×àÃ7áxI/øBɉª ‹zì/bå=ìý>D˜EC¬)jLû.+îCBZ>sKPëÞÆ€{°VÁôP•žÃº – 0jømëvÙ…&rÊ X^¨ï˪Ȓª²l] ‚EÄ:ƒv¨øDralNT1˜ÚŽÉ‚Ú +ú¾ÌvI`ýÞªaÀë€Äûîñ ¸•$UéJÄ÷zÃü*~ ¼ˆ,ëf´ÖÛRÒ9£ÞOí| +bGìÏ›¡¾‰TDC~±÷äç¡À0ƒ>ybBˆ¨mþ¼d1™®Œb†¥Qôs±¨N©ÔïyÞ× +|ƒqo«ªžbm3:R®&ÅᅦVUo¾>äN¾×£>Žøc“òEbµü. +·Œ!RÈöð¦C8!= +ÜÈ¿_kJ +el}1›ã»`èe Ø¥Uäo*â7oÈ-¹ÐU47œü¯´6(AýiAÀÎ÷|d’aƒìO¹¸a#agö;rÆÎfßxÎn¢åxâF´ï"†¦„o:î!ä-nul9ƳèD‘<ÈÆÚ› ùç\ ü³ù°$þR•vȤFHžØ"~¦O¦h*ç¦,$w]²›ÑÒ…ÆÂBÞ4ôuüx¢H qèìȸMmé7å"DÇ—Šø9›êb¼dèi¯Þa +Ô3‚]¥cV}ñh’ðO`í~ª’­š4µwìõ(·ûàr!]¦0Õæö˜„¨@b7ŽDrÐ +iE2²0Ûµq§0Zó|"Ü©²°1k”†-Ûàt$Ds5W„jq«WO¤¡Þíãö!/Ÿ·/I{÷bñ:þÌ«ø‹¨ÿMu¡H#…âw¼rz*ÍÌ× ¹TûfÁ¡:Ë™Ó2tI~VJì¦OHy},Ð+]vwŠpJuç©4¦ÑšÏ´[PŠ´õnzõ bƒ.š=…“cÒ~t’³Ùª•¨q;ã¥þ<2!ÆFš®ž…5z‰þI'†¶Ê_PÛ¨Ùª”f­ ++‹¦5æ+ÍpK›þXµ¦´§érÚxÖÐħH¶†©ìuÜg›Ó°Á¢=]]oy¨•˜¿ûϨÀ¾œ0þoVXn“ƒ•;&šíyÖ†0÷“þ +“‡‡Ç™äã‘î³ Ì“âr«Ô¼x“¨‘¿0›È0ÜO,´Ž‘Eß©2é3Us!íÑF}÷R³QmeÝKýûçAÓaØgÆ,—0†ÌŽlÅ·RÈ;++­ê3 øJÂå#¬ÖJ;“93/ì /e&{¿”%>J98`Ûv/*ÆŽåû‰Ç®5f’<0%ÉØÿÒ‰D\‡¯³smïWvî—,þú!¶ð ¡^_§ÑŸÉËÛò‹©­žOMFç +o«?° Ž?™ýn]ælMõ’ÖOû÷žšmÿøH¢Vã¥]öä5ögÓúyYø'1ÖÚÔÍC@,ÇI“FšJøë©ÈàÂmÓArÖ"¹é¿®›ß0ô€8¯f!y”øúØ‹I˹|ø¨Äçáxˆˆy6=ÀHò"JnÖ%Ç̾œQ­Ð`è\ǼNÀïhñ÷ëúŠÿþÏÿýëÍš¾þ¿×ÿóUøßâŸõ§ãùþ÷Uƒr£hjvUºq-oÄã¼åüÿëëÿø_½ D h ;0þýòøõŸ¾ ʧ—u¼@€ÿö~þ¦ØýïWSÍrø‚÷!ÿ¯wÂó˜á»šÇ<àkþÛ +õkF?~¿F€ÿx9l4Ðõòß¾Â?È9´›íÃ>æðßîH~·Ò“ºßcÈÿpOÞ/{ÄûØCþ‡gTFU'¶òÎ(—–3Ê¥}Ìè?yEÎ%_ñ1—ÿä“EÝŽWò?=)¤Ý|,Gÿx.±¡!ÖïWòŸ¿ÎåòžN’ˆfîç•6äˆ/AÎBƒ:¨“ š^ðÿþßï?O9¶ŠHþ ôµ»Ácš²0b+Œáœ(ôÂÝ +™ª5x¼= +cü´–ão@ô?@±&ì4Á¢fÌ +E±¿¡û˳zäqjø‹±ÞY\üA™³Š@ÏPêp7` +FGÁ€ø‰Í”ùóÔüÉñ+O@DÄà£Ã€fɪ +/1ž{„œSr¬œ¦¿ñWçFÁ®#n­füëTÄÛó ø®ØãIôõðí‹¿ •—e¿qØ°Ú +´ä/ÂÛS|¢Tå/½¤|8>ƒÃycšŸÜêIÁâfÛ1L›>5¥8æFP¯óbhK{Œ½~yï +!cL½£'ÆÐû:Š ꓡ̸±ùÁûy^t v•Ó´ w˜¡€˜±Œ'l;lûk½ŒÈ/‹—Nq‚xX-Ìxi˱Ÿ˜ìC Àâýªú‰±fÊFдÒû1«‰è¦˜ô­lê?ïõ߯ŠÊ Ü1´]:.cF>4¸*ë`&)ð‘DÉõblZÅ°ú¹\ô;îOÐã¸ê(;¢Õã¡dÖ½U:m|1î‚Z,ÛS‹ÝǤ¿øPåBBÂwèØ +Šonzãi^|k…I5V¦c‰ªvI' +SÁ’󉪽àG"g‘³Š\œ8Ç•{©µ¬¤raØcÇ÷™!ŽoŸØéCsfZ5oJÃAWÔr4ä×!'„¤H×â×ö~ßHÉ HãiÞH@ßDJß¹µùÒ´Ê)R„o„ ¢0›þ +¾¨þ¶„ž…~،ؠ›Ì¢âÞÿ™ì]«\Úcì  +Q{Ö+/ò†5µƒÿàUEæ©’Ê G];F1¼EÑ +¤•‘YzÕmNÀ]t ÁÙ”ðì12d÷s¤ìÅÉ[#åWÚ•¦L]jÎòÆ2¾ +á´úÇïÁ{Ž/øÂìI„v~N²DÊñ¹áÿ±R(lÅÇ+´Wû(‰pNÛý9â*~: ¿A=£°bE+ÊïÌ-dqÑA÷C¸ÐXœá#ãñ,»ñr +ÂäáaÈN´[äNEŠÂ ì+ø”¢¢ÒJœþ¢Ð;ø'¤7A^ÉH„銩Þ„ÃìÀz)mâÏ‘çc +Wƒ·ìýÎ(5Áõ×G_‰Wù4û!AåØÌtÖ'¢âÞ0Å)÷JÌå>ω\Òfg©Ø¿NH”S[Ãa]Hº Ň.TR$mo\K™‚¸ÇeGC±…qÑ+g­&îñòÎò; ÝÁ‚º*ö€ fMVþâZ´ñ‚&²†ˆ”F˜‘ÐXü òq4¼(ÐA^A >Êjà¿Ñ%í¶pΓ,)ÍÁ½MéÂz·tõû‚íÑÃ(„IhÏEmXj楣!cƒÔÁM¹7>{ +Z±Ÿ­ï'ÌÅb {ù-ÐG¿˜zgdž‚‡oq§Ø@„ÌsJ(0 Ö‡˜'LfXqt^Ú{K;û +Ǥt!‘b^UÁ€‡Â T¹|ð(;ƽ ß/væÒŹ»‘@ Ë^UŸ·º>ÆDû +‰Ô±AÌjÒ#Ú‚xŦ•%Œ/7Á´i‰§9£)zèw¾"Úày!æˆçó=Póñ V¡¯ä!û@oxØ(òa›æ‘.ÞˆŸ¸Õ^ç- I®òFØ Æƒ÷0|œç8TÑïÌÕ)PFc¿r'æ4¦ýK‰7zƒ0­P| `Ú1«¶@N„›…O„­•È+2Š  ŒÈ²Œ~q +¡œµ +M"q ³y¨óü{«þ9IÅ•xéßÙVRÏQ¸ªpR‰à"Ry¥ˆ‡³KœS+ +"7_AMpåN><Ѳ˜¬‘°§ñ+X¼Z´P±ÁÓæ÷jJÌo&é¸ÁXcVµnòŽNϹ'µù禌¢ˆë‘¡PLæÍJÎx@–Ñ*®D!]br2*ý¬C þP¡¯ù +Bl·ã‡XþÒum:¢ÉñänZÝj45ÔYÆ•ÛÛȆh‰Y~ )‰þMÑße±ùc'é`ã<‘Gß±ZHÇboÈýÒ–ãëÅï |º±n!¾;ø‘’3EÐQc%}¬aº±Ö:ŽKû§k¿Wæ:à–`ÃV\ “~-„ß_XDèœÃ’˜å9î±ì‚DŠû~Eu˜y_…ž†ž%¯8WñÊñ÷±ÌýD—ðâ7x§òÞÊ=öšâÎçi|ÿxBCT‡-HµTþ¥àÝðË€!‘ ˆßlÈ?¯Ô"÷·Üz!•üäaXE$wšÆÀ5¯z˜Æ¨ÏD~p¡6î~ Ãk­Bãï­UO@"Æ hä…9äø†"1s² {’r߆¼‚Å/(8ߟØb'Qä·'¡íu¬ý„+%D;ññ +îÕù[<çáýëë÷ãøþñˆ¨†òø« v,Щñ¯=¾l‚€·|@–=£ÀÎS7׎ê;²:uaTÊ°ÜPÏglž†Ux —ïùE{$4‡G;Xß"9Ûâ‚ +:(QÐCé¼Å¸ÿ=ž‡LfÈÅDa†¨QmD §÷JÃveÛă]Sú›®I¡ˆŒ–z[ªŽ`¨ã¾ó$„¡Æá66Yæ “†qô†Cðñx¥%7¤$qÌR…®ÔNc*£&çqó@ÊMQÏ4 Jpª‹c¿"yS-Ü¿G.ЄP +–,äûª•â7w0L¢ØŽJ‰qõõ}‰=^D É€<Õu±øN¡6‘0‡y\’Ô"úS5ž%I7NU£LÚÒÈÃÃÀ,ŒáUmáTpoœÚ,ó Z©‚ ¸¢ý²² +0¶÷ò´Ð\†Å*8-ÄÆ¡ô¾ÅÔ8´’îÐ1NY jù8æ…XÌëSÿÀYsçÉ€«¯Ü² +P%¡²=‚Î)¡> 6õ™’¤P>_¥U0žD‡[‚fÓ1ôá_äÑQa‰¨R!?zëZ2j,ЃÃç;Ñ}LwòÞ£iùÒv¸¢÷#*ÀƒWpÜîz"eX|¡)ÐUãóÄÙv]ô‘Šj‡Ñ(gE-Pu¦pH|aÁ.þ‚(6DLøÿ4ck¶7û;!Õõ£i©†H½¹¶ 8’ö;äiScÞ•‹ñð)zñìû k½µ2ú1[6à/šˆØ1·¢s­Õ€Ö~ƒ ÙECÑu‘5m1ÉÑ“÷¥CBÂwp ;+®ÆX:oàsh!è›Lˆð«‰B7é1ú»ˆ +û ¿p¹GÔ$'Q9œèÝŒì!Y=Re±_ƒ<3QCQ£.3zMdBCb« 8}Øê=ŽáÛ?ië8êþ¼Á”ìõé±$Œk¼$s7¦4™ß))Šwp«©É‡3¸cÜ$öŠ&ØF1Eñ'Ìa5Z?"îM‹ÛÕ›IŒä^„TÄ¢ vÄÆ7zˆŠlbîlM+H +â V=¨3ËÙ:‹N²›mQ£c6{,½>>yY§QÓ½ó¡ð¨26“Œ`º¥#}xE䨣@8%6„eíëuÞ±%"ÅUrOá÷¾‡#Édž&Ý•ãHu·Ö‘¤XêîÐ5ˆU°" +3¿=ñG_d*¯aÙ¼×AZ6“˜¬ÀÊ+¤¢lhê=RåL¡x´@k#¾dwÁVŸx)„ÂÖ ä{­ß€Xn„tÕ¶À fŽg®¾©U´¨^±H£¸a, r¥i°±Z?(> ªN˜à¨¤+ëÓ-‘å‚Tö5×ñfUõçàóѲʽµè9ùs³5Ynb ‚0ƒÖLJPµMXÿ/~V×!»Ç¼$ÖƇ·øEŠŽc&zÝ…s× ÂŒïyÕÛû> îQ"ΔNEÏ +jÏ’…aÓ_=øa»Sú‚Žû9¤O•*\z嘇×M>ÑYù…íq‹³rœDIˆv8Å.éxå8Þ¨³!°À´®ýÔµ[§ËZ?Ñ5‚7Y¸ÄýÃÐŒA똧(šç€Öˆ”i,L¢«DÕÖÅÿ +ð­§îíC†uÓßp#èÑÈs‘:j ¯-…[ +M†ÛâÔ"¥íå¦ß2Ñß5Çßò0•'$× ùPhDí×oŠ@üF—.æ<覷/+\ŠÂ•¦hãÖEäMùCnððIÊŠÖmÝ/Éz}$—à^!c†â$pð”ñÍ.±¶=‰L”c(gÙ@dáï‰(€”ŸH–—SxèM‹q#…Fä""N¶Ð/›’/tö%=˜€Îãd ìÊ/ë1üZéÞãXGKBáCå >®G‹N›û¾¿¬òýÑÔ†\"¨cáY5“£\ª¸ VSæ$ CmJy’¦27'¥Àï26Su•®ÀçžWPG1^øJ‡T$[ +q¿D‘ê ÷Ö‹O0µÁÅG,±@o3½ ^ÇdÓl2vN —Õ¶™ìBœßÞ–ëB£gÞË‘«G•wTÆ ‡Vª”¥¯e É ©lL¯ísL‚dâîý.·9(Ƕ‰Vù¿Í-x.]Õ!ykmC¡˜1gI +ÚtÈìnÝè'tìcs,NÓtK;/â’tÆñwòÏó ›ˆ»vS¬~ÚàÛØà»\M„šµ™ôÁ"߾̭ˆT6­,Ò>1EI¾_û#¹‡ï¬"lkx(õ¤¯ýÄsôdh¯æzrR>õzÑïr¸1>1íGä{‹8]5ƶa˜§I׀⃅ÿŸ>@QðúøMcÄë¹óýšßؤÂPŠ +AÌ "Ødá¾|féœÛ±|2¬Wþ`Þ÷¡ ÷ö!å}+ïùx‚LM†ˆÞ¤òþ¤?ò1UÆ,h–UA +6xK'í-]l2Š÷¦ÎËf”ñŽ*}ñ¢ ¾Ù¼'juòÊ¡Æ:/ûøx…#ìn~>AuÏs° +VSô0£ÙÑùyåý…ôÛçäÙÏ):8 W‘Á¹NpŽW Sc¨,]ÎbïýX_¿Å÷Çó>ý[±§p\·­Ú' À\EÁýyÍäMÔÏ~‚tø•ø3ÒCþùsB1Ç–Î7ÅÓ7¹[^'ˆ(‘úÒ·žðÍd­Ë|uì[’ÝïÞŠ­X×ÅÍäÒ|©=~Vò»§1Q9D·Z}[‘ºmdb®s,bEY棟 WÄDNKæéÛëÓâGdku>).ܬ§'à›sí'~2”[Uâлr3ÚØDÈÅGBùø…¬Ê‘Bsè&ÜF¡r1ÌÍȹQ ÷Ä¿ —¡Ê¬ ‹‚̼Dk3KÅ·½·i~¹m¤X2¢!;S±ÏNä?”ì7D¾û6•{ËáTcÄY1zë +¸‹ü7Ó;­Á3{Œóy 3¾ÐŽk±¾g!‘2ô½­i¶S»Ÿ-4*øª@oËåèb§ííÒ0ãIkœ=·Êæ|À¶ío¶QZÛO¤Ï)ÅUó«å¼ªNøѨ0ÊШ߈Xú‰¸a<¡ûì’È7†¬L0ÜÇw䑆CðnÛ¢¬Åµ¿ä‚GTÉmO1h +3îN²ú) +òô$ñ ÛžM”% îJÑ•›LÊêM~~Ë7UÕ»%@϶»2W-’iõ³«ë=ˆÃF#•.ƯpÌe«¿’·ç‚NÞþû*ß Jú"¹ð{AŒňu–=j +“Ñ# +!Ù”: +«¨¢Š +rQedž»(³ZûǨưìÈe;¡>R²§QÓ`Îiî1íHaUíV¿áêù кÇ4‹ ¥Ò,1fI¶BûÁ² +§…¢øxC…Úߨ6ob{·9É㺠+"@Ï©~‰‚ZéÇg¥Îú€˜õ˜÷ UÖ ¦“¹ôýÀÒ½ê°Mäîù5ÃušªÄ$ó¾>Ç2kû|@ÉT¿­ˆ’Ír|á±Å('Á‚¯{’Žñ2’Žå:EÇò|ö³¿óÉYx«=ÉÏÃøù€þFÅ| +ÛŒÿ÷Ë ¬Êòñ–jMQ%G¡¤R_ž¦Jš!åÞ,”3EP°ª+d–'p³Ž¨þzËwQ™r÷ܲ)Ûj£úmèÿAª.•cW$Š qè|Éî¹|ƒœØ*™+/«ä¢¨škšßùÄUç1—ð¡ƒEé/s8k…X0U½Ìx'Ý!öQZÖ£êNi)Ï×ï'Ák‹û÷ë„ÖÚÆI•g”ͤûhî%ã»k¨É +[8Í»~è~oÈ#NŦ°PY˜`‹ˆ³uPÅ›¨H¦ÈV¬åˆ{¶SGzy®‡æþÜÉ UERÝQc;¥eÔFQR™qž¬ñïX\áÀ†À@/ÛHølŸ›äÖ@õl·dŒI×N^’y`¹/Î÷ªZ¾PiYšÌ³—]¤É –£´fÃ9ú¢ Ž±³E’1iùý¨h×Q\àiéu‚ƒAøB:Ý__¿£ÔÏh„aJ>£ºô/®O>Uª„¤Úõ I‚¼t(púÙOKóúzþùaBG®Aé(j¿s +øµ×ào 9Ù£Êõ‘kÿ¶k?nG®AÄÕ—Ï\ƒ½ßøño?R +4þÞQüLjòÏß³vÀñ% +ä”Zà)îa¬áû€Ü(·¶_p«Œ‡2 +Ê +ËÑ÷1‡[ñäžãÊžZÅ+ßÇB÷#Mõ+¸SçG¸—·Ô¡Üë˜ç×ï‡ñýã™ñÅydp¼3 +¢lÚúÈ48 Î4xƒ¢õxf”NÕL™O÷G¦A骣LƒÏ#Óãø3 +ÈLƒxc92 +bNNfÒÒ}¢<‚Å`{?Æ5M¿? ÊÅœ ëã8Ó õ>2 +â +̹3 +p%®iàñ÷Î48á;Ñ@Ù™xãuf‚tOš¹cŒö’ûø +êú~Ä’Çf”LA2Óà=®Îç¸ÖG¦AìteÄØÅ€€oN8ŽLƒ˜Hïg¦A@Ú<2 +è;Óãrf`ç“™#ÓÀ?âx|”/Þ™8Ç~fd]G¦A|+Ù)%„4…g2Ó Æõ92 +°ýÌ4#h™i€í;Ó ÆÑ{š¿ð•¸ê™iP:ƒ^reïC»>2 +2ŽLƒã˜™i ÀÏر3 +60 ¢@é¯à RŸŠ²ý™i€M½ÏLƒ€2;ÓñìLƒßÏ™izv¦hg`ÜÎLþÎ4ðÍLƒóÒ2Ó`?ÂL¾bgH©L@î#Óó,;Ó+¹ÎL@ff`éóÈ4¨¨©í¸yžŠ2ù+ÿ&Içh³¿ì#Ó`óì„@ý„ B™˜f=2 +ÎÅ3Ó ¥ð'\;%;ÓÀrÑDÄŽ¹k)G¦Á'„\Uú 3 +HÖŽLÂufà;ÏÎ4ˆ1<8Ê4ðXØÒš!;Ó¯†z-ãÌ4ÀWkf`ñ…IÈ4À4g¥¦\±_yf¢¬3 +°ã÷™i€3G¦Aœ¶R—=lÏG¢Ažô?^àL4 BíD¼¶1u•›!mSÑÀ¸áN`ÉxÔ¯Þ‰1î…ñÄ" âJ4Õh ÄEÙ"< +`ZÌD^¼v¢Æc'œ´X‰Z÷N4ØÄYRpçëL4¨…]š2‹¥äDçïÒµ;J4(]99J4§|&”îÄäX`QžÁh”gà¿Ï$öÌà±+×GžAqµò bëÈ3ˆ±3¤/ŽßLýuæIJ…„K¸c%3åÄøþÈ3(]„ª€±yŒò ’Ã8Ï ÿ®<ð¨Ì3àV{éþFq¶‰N©#{l³! +ߪ6Ö,Âe–:çˆíM¬„i—L3 ÒÜ·ã¹/lgC§`¬Û =gš zâAH"údšp¹iÀvf ©ªd†u^°O”Š¥å]«TI–¤‘ ½‡˜÷Jž»Q†t˜¿ -mfrÖ2ÍÀÏû„ònÇ÷Gôp¢Ì«Æ°3þ?5¼î8pò—ýÄ-­)xMýY@ÿôt&*¬" „B ìn´Xb + +siØ NqÚ“Š*c‚ž”¨¯ÛéMÌxÓXn6h +rí±Á4T yž +"°¾œJ;>†ž¯è¨—zÔcUèlä÷„‚¯ J ‚"¿’x–Ó(h¦€±ôÈ­C¼Ã}ÚmÓ A Ë·TÛšõy$'ä¹­%ë¯@™J} £+f#ïCJßÅØ~S‡58†íMBPMmÞ öGÐxŒ…jKZ²HN ßÁ茉7AyÍà@ŠÂŸA‹ÆŠW„ÜD +SYGÈô¼˜Ž‚@Θ:ÊÓ´Tg©¬;•»Q™}Bý¾0¯É'VG¦~á£eˆ½L“EA%ÇX!D0Ô¶w^d;в›õ®š÷Ä÷?$·Å‰W1*nW±¬‰“í/½¢që© ®Á#éä¯^yWAd0PžY#{½&ÓFªØï5òBÞO4ñì‹ÅªnÑÊó'É’õbcÓ-`wðÍÃЬjU¦É’ìø̱t½—|Œ'Î"m‰ñýÄxIrU91¦ž<õœÖXø;eÎò#Ô|¥ú;Åcop×J5iƒÔÓ_ïkBÙ-Z8™µÄ[%¸µ-<cÖ[Å’‘m +áÎy­¥RšFÈ&ƒ½l<ÿ—³äÚ(è‘PfžŒ–ùSRT•ÿ$¹Weÿȶ¼¥K…2ÆâÌ´î9(ËßðeuF–µ|³'±ÕM–]³°áþ:ÐŽ{Üeiº7Ž©PKL¯®vÜDµçÓøû`¤~‚íëøR’:V¥*Õ†–†%zßwSÅ‘PÄJz>.s¿„<—ƈꮪÉoTÓ«TÅ—7‘äÊÛ2N|Oæ'E[¥Âbq¢9)yCí¦¾Èxö3îýDÌ`ùÿ2cè’¨ØÜ-v2U§ŒX½ÙK4௃Y,Ä7§ÀÚT«k‹´E½‘¶±[ +ƒ•yΗ• ³KYô0˜›4ìxÿQŽ\b"J÷ +18~l#m>uæ‹#›zÂxÀ9m€×=R8bä¼5¹&ª/¯Uˆ[^E¸9I2Ea£þ§ôণ©'7õBÚ¿%ãëíÃèɉ›„œ9IöLÂ×ãñë·ýæ#¾Û¶*Tª$±»¾‡—?‚"‹q甆àmz£Œ2Sù)÷P¦NK«,Æ 4Ü"ÉL¹: ¢èSíáÒŸ=ænYª©®èÈc-™Fóè'Fœe*WºD_4«~©˜Ô7FÏþ~µãº+rH4—ñüCò¶èvBÒÝÙ‹vMײ£,õ B’e¸~‘êÒ§ ßÉl¤oõ´-R…˜7÷/kÝ¢5êßÚ\w‰{ßöL½¨é²^Û?6$6IFðÿZCìr-o—A7‰ÛÓÞDϹõöé:éݘnãRAþ°ô4\¶Ý'ºÝõ éöí*Ûg§p—œ^ã®ÊÇ”8üsуý›f<…•‡î¡¼Æ ‘×8_ ¯qWqÂíÐÍ9Èå›s”K¸«:ávç2÷´ˆï7pŸÎop'í4ΦÓø·£øþñxþ† 1Paw©UFBòÈc ©ä·‘µ£ãÌ”åÎ"®­Þ¬.äBdábhö†¢Lƒ ôE#!òý]‚",Œúß©RYšñÕíxÇ,¾=N¼S ªwªUâ¢_³Ð^Œ±Ï  &'÷Œ¤µINÜhåL2˜ ç¨~FæCÔ}ˆcUáCüMlhYú#Î .Za DDÏ.º½œ“«l2Jì U8 ¥ªl:jNÔ]^™Ÿ]˜_‚:Z5Ðû㧠›Ú+ÜùÐÏGÂØ€4’.d!T¦Ëêt# +²¢¯8 ÁZ°Ð·c¼1hCn…ãp‹‡ëz2™X·Z¿àŽBìLŒztØhTçeÉÎœ +–Á™€ mÍ£näìððo6Þéí8d Á+ß?â†ZÜDv“Ð*^* +¹ÍÊfò„ hÎÆyT] ìRed`0‡ÆÁ㎠+¿Ó·bb ökÓéôµ‰±¼„íx å,X4M)u1«^^ö]F@aeÛlÔHÖMØ!öxü•EÒ (*æ ± +g#_9×éeÑä‡4 –ûÊ£¸å°/Ìáø¥¥#aâx„ï¼UlëbzŒÇE ¾c6FN܃„–EõtŠ•µh¿®ºçL±æɆúc#wtÁÔ€oYºÖ1¯0Ÿ€Zª²{y¸}C¿ fLõ™ÞÀÜn\™ÄåÖU{Ѫà;‘,:vDÑM*­¯ª AÝÎG¬\L7ÛS#þPšý•ÎÀOHh½øFDm|gqÛI’î¼1’ùã;•ÊV·ãëS謊Á'„(†ÂÄ@ Ñm +. :g +A&ÑÙb1 O¸òf™5P Ÿµ•‡'´E¬“(Ò]š~aŒhÑ;‹-w/÷x,) !>ë~¸Èᘼòã“\þ€ Xª]Ú“ÁÈ´vå·"°è›T'©Ê+m2Á«ÒJßÃ$-^M>Ñõ +TÔD,¸DýFReöYäaÉ@TÔDà†¤&›&/¥¢þ©×rh’H³xŒM0ˆØÉëL!‘üi}ü†,…í-æW`:Œ'HÙgxZ,¤§ &e(×gÙ%ö¦Ï¯C¸‰ílóã ~vŽ“t#¡ Ö©47ĸZs硤È•zù‹ZÇË,þ fô¤]bPªÝ.¡iŠéMõŽB#ûú%Ëi—tÖ•´cé›)-æ$¡ù÷¥zã«YìÓ_¹ô“[ÇȺ=6½Ù!@u¥dÏ®UK(=­èÄw'¹1YË’>s%ÉÐ÷êQ-¡*Ï9°žCú/1Ž‰üÁš–ô[ªÇ­¾áüE,ŽÈýºËÍ@§Z2³µ%ݪ82mEΕj9%, +’28ÑÙ·X"lË1w#¥Z=ñçTún“d~R +OÜWkóíþ óts}rºw~çÁ1Š`LEø)Ø…Ž½¸¡ì*ïjŠ&ƒ,!)aY¯‡ ý[lªla˜Ç’>üØÒÍta/Í"÷Œ&ƒcI- 'ý&œC¥¥9ÚU•ìÛ¶BHbG}&’&˜ëQ“gŠ4M;œ¨PÚ5ÓÅN¦JìðfÍ¿ì@£:ÍŽ'ê#ÖC^Ë:m*’Z‡ +&JðÚ¦’Pºï(cë!í3ß'àV . D[5¿ð:qÍ“xèÚØ™"Ǩ±…¢†FXËt¤È+#"äwˆ´¿J×—¤ÊÒå‘›¼’pÛHeBPxèD!ìÓ£¢˜¼kõk›‹. oƒe×<ïîP"”ƒú͸ ¤(“Q>2¦Ü¶ÆµGÌz?ReÇ„å3"¦š¾ùØEh˜Vé¶pCYÊíÖ͵Ï  ^ï ÃÐ^*â­ñtýõ ÓÈñðßóÜaË<Æ’ŸéZUÑ ™kPYyC|ñÇ_ªOËÝrM¯[m +‚} ‰Ëƒâ2¢¨øQaßcÛ½ôG´ÿmØlˉ!îıHÃsM¸*ç#R¦x3ËÄY:š«G±Þ‚’ö)9Y4ˆ›É~g<Ƽ]Zš®ÿ£lg2Qˆ¶¸*±E·ò¡=<Ö«ê&1sWe/ +cÕ¢á£}\ælKñ>¡„³ô +? p‘»Ÿ ™ÑÇé•gÛ U¾ÃÛãk€Ôœ’¦ìš ´Ã ·PÂ]3%öá„*,‚\¯î°Œšª0R•+öæÙ KÄÞl${Sð ›lc®8Û|Éá¡c³Íh?1dáQýǪlWG¶jwØ»Ê_ +U ¦n ŠÜÍÓpĹ¹•p ’dStË0 .r³§µë + +òØlwKyÌò ˆ©Ÿ~˜ô'’ŒŠÅä¼l?÷”Ç#ÚáÍÝ JjMqø7ŠœN{)´{ä<)³ŸGüÄŽ]%B92„¬ä32Æ»¤¥cvNOº<´–ÍïÅŸ6YîÍ^fwên›õr[Ò@õ©ª&qvw¬ +zp€ãžÿk ‘ŽPþóáiØvBwÿj«š²”ç¢éW`uÇ(6¥ jëÁîBm#\Mþ-%/,ñιïnÑ©úýŒÇ2¾ÅÃ÷´÷ ºvZ?®Ä¿&Ã&È]ãÿ÷`Ø“àIè.å¢ÓýÈ=§¤8¶ƒò(E˜¯²ÀñŸCÉt¸t9ŸÌù`xÀ®=S³ï"£øR þ§Fsx˜èë:ýªÍ_à°OÈÏV½R³õ¾½?"Mt#“%¢›ÞN²uDy@‚àŠW$J¥øHrÕýP!Âè§e{Bš>Jr‹!K +'ãõÎÖùFb‰ØÓ³ìæ{I>]¦ ëb˜­Zo¬‹áòú~µ ½ÚeyImb]½°.&v¬KfJ’σuåºÜ÷ºrc]ì;ùºà6À…u1´_}c]ìCåºàŽØ…uQûó`]â a]xÍì`]4Øσuqœ¢À-†^+ëbwhÚ ë‚k:ëâ7ƒëòýXI/¬‹A¡úÁºΩÞXð a]D ¬KVô%fª#Büya]²¢+Žu±v»°.Ö‰ýºdf´»°.YY‰_XCÞ–ÖE”ÀºX{_X—ÌZñŸÖE×ëb7Ö…uñÕ¹†lÜÖå{©ÞX#ì ëòÝΕ‚ž6J_ëbíyc]Døè‚k·/ ‹.>ÐÅFÞo ‹MMäp½L9"  òU\@Í÷çEÁª½€.ZÈ›R ß„¿èâU]® Ðid>çùsp.&¯b,Í%ØéÙ‡–ì…syQpÊ%òo FnŸý80ƒã†¹ä¬‚¹¸5é0—co:Ì…” æb„}`.Çxv˜‹nóÌÅ:!Ø a.ÖÉäY~:µaT¦“Y>ôtÁ\¨îõ ç§Ñn˜‹ßvu (¾Òÿý²/˜ Øês±w#´#˜ TÞs±žß0$B90k."BX´ÌÅl&}0Þîº`.vù5¿`.FqtC“)¯õ^/yì0»åäµ4Õ-ÇCæbw±æ ær%)qû°=¯¦¯¾SºÊ` å’» +Dîcõ¬7ÊÅa\(·ZårÌG¹\OHê놴P.öÆùB¹øõ G¹˜%4.”‹µÓåBí~¡\`¡s·Ž i](—c ;ÊÅ(ãF¹¸¾q”K¨›@¹„FÊÅÚíF¹ØWÇ åb½@.‘Íæ´]ØJ/7ÈFÞrÑ?!Ë7©\p_ù¹!‹1Ó8 \G¾@.Æ{õr1î\7È%.å äb^ d€¸@.¶knŒËµÏˆqAb™ùÛÊé¸Øæ/Œ …ÿ…q‘>¬ǸxjǸ˜xK7ÆåH@Ǹd%lsŒ Žõ—„ …áˆáãâ¢Ü1.—(Æ%„½0.|ÅÇÁ¸\jI]P Œ‹ßËvŒ‹\·À°ëÊë…qÉžËjÇtÐÖÜ1¡n¢ãâ&Š0.n8ÆåX(Žq¹ž€?‡½ ã«èq±^µ ã7±ßjà…q Õpa\2½a¿a\ÖQtbE–ËÕæ·OÛYÊ’EÍˉe…¢ +W†÷¢U8êèa€'–—Ä»œX*vy±pG•ü/–]s½œXuÆŠK«0îy9±,€Ø.F$Râð¡…Ò꾬².,ä–©1ÖOPÜžä +ÔcÉíðAr¹±qe¸Ù2ýÅuÜ:wF§/¿Õ¹ƒ}9²lìÑF¬µ×C×\…«Âµt¹±* ׄË˶7VP»Üq»±Á>^,êÆ–˜,tâ^,\cÿ¸œX~å9œXÖ7íÛ¡Bvì‰;±‚N,û‰Ÿ·|Ê€*p'–õ¨ÈVY”yäÝAÅ‹ñ/–óo7—á–L¤)±Ã‡e+;xì¦ËXnPù+Ø>üs( ;2-–½Z8\XÊf>¬à&ù°”åçrbUæ>N,Ę?‡UG˜´2aëB”«Ž8“û£øÀíÃúÉÿ>Q0Ïl²‘=ëB?VãuÚ +“7¼‡ØRΨf–s²"IÌ^NðÍO]Èž÷ßÝαéáUèÁU4¢-‚*MÖ¦e‡Þmçl/çU\«Ú]ÐîFs%” +Ä%À¹å…ÈܽūÁî÷°dÏ«-sõ¢°[‘µZ) [‘Ô¨¾Î(û¦³@«W/”Dñjå±e§´­/},>ú/:‘Qým0çâÜQI…v°Ér‰I=Ø +죇E‘̸(IR…CÄ$uÔeSxBgn£$u- ß ;ûÄø£"ºZÝ«ÔyÆK쨭º¦ë^Nv ¯^g¹™h×<äÕr﬇z]…YtZoœ^–¸¬ì}j.aíÍ9%Ÿ{=K].PC#“,±î):×ìXXO´àQ'j¤ÓvÇ¢ê;ܱ”§‚m Ùb‹~ñ´ÚÌçlU’JÜY¸"V_) §ÊÓ<«T¥-I_W{ƒ]NžGbB[´E;{ ²2V4%´ü¥òÆõ#ìŸ?¡ðŸ83ÖØŒÄ ŒL£êHùp¬i¯¦ÜR™[ì +†uó{»øˆLh<²«‹N,HËárȇ}›üøþ +":)Ÿ«ô]óš×[¢1s=⌘…î™òs¥@øIÙá?â®2Ü÷(’Ÿš&Wkç~0!6ÓS~_v¦SÔwf9cW(’²óœ­dƒµìŽ“"a™~*D‚Y[¦qŽÝáQ©¥¡Ú5žZ")I ¶ˆòßÁèjå8ËÅ7%$Rñ[EYÖ¯³5ñ«3£·][°+WÉ=:€÷Œ³³ 44?/§û˜‰UÁ%úke˜Äæ¿á›ÖÈ÷e¥ˆrÑ “ïÚÈEÙzz›î¶âOüxœCï7ùô“€=jÜç§ý-g%‘pFiR4S)¬žèU,¹Ê³¯ÙŸÝ|ö;ï±–'#%á¤Wz—ËÛ”+Û¤|‰bCº'ŸuÉîß(&Z¾^¿å~QÁ®ùzQX +µÅ±®˜ÖÜL/´e[SM98›3>Ì#„ŒÀÖoƒïùùDÞ–Ö#Xí©Ÿ¸'TnN²¦l‡¸Î§­{,2»×¼yœÁ5f—™®$XŽK‚ÎŽJ)Û! #éÇ…aC§o!Ž’RªóõDSh‘¸GdáQàOÜÕâƒóm0ùïnËt1MÚ*ý{8‹‹ýß/ÜFœú=…SQ<ÚS;ÅZAÉ%§‹Â\>[Ó3À\L DŠî~ö„ÿ£QOµ–ëÑ^ +yÖåõžS¥1ƒõÀŽšnKô§î×(xÁDS‹ó0ôé­¢è¢ÚCzE”$x{ý +ºŽŸÎÑ‹ó9:‰-þ\ã.›c¤¤h*î7¸ˆOXP=¸&ü±Ÿ¿.Ž­}áN +$•KÌPùøº(¬¼üuª?å9¤ä¡ÇøÙ_”Ät2|‘}=ÿýÝ!ëc#UŽ…ÞÿÙG¾X¡CßO¦sú$TzîßWH²+%w`?Ç\5û`áĮ́g?¡ÁÁJ“#|ŒK|8-Ñ‘ªËçT¨?M‰+¨ç€¶7v73Ú~PTD§{Œ³Ñ麺`¦"òUÙDoã]¾}R¾^”ÁÄaÝCÏ p$7;™‰WH“…€U õŠsÎcc¤Oì0Ó¡$f ŽWÃrT·0 +ÔPÏ‹4¢GYA÷â/jœ +òlM‹ÿdiEeNÊ'ã2ˆà<–vE³¼ÞÝ¡KM)@ú*D1k \‘Ãé̃ ÄÓ”]\Èe[ñá?ñGà2èîÓ+ŒAô÷.Ôæq´ Ø9yËÅ­Ü~°H‰‘;0°‹š?¾Oß!"ÕÔ¦ÜCƉ»†ö§4_ÅiÅýɊ»ßí " +íöƒš¬AлVV`”î!f1oD´èO¹d²j‡(’@éx¼¹‚2ð;Rp5Ê„»Ì´á\/üú8oîXõ/Š]ú•1ßÃÃ/ÙÎB¬éPN‡Îñä'ÅÕý5²|b×ö_”Áó0¦ÏÀ¡À¥`Õ…ÎD¨aÔ€Ê7bù¦C-'Kö®<”O¬îåÖöõçXNþºýã~Bÿ@œÄ»Ð­Î.ž¶ü"…®“ë°ðŠÆÓ¢by=ÄÏk”纄Κä•ãLeJ*,ésUÚò^ +/Tèn`ƒ. +kIúk eñá8ñžbÒðÓ÷B÷m,Qqáéã÷|zð‚çLàùF Ì{ÑB¯{MLoË>Ú ²pc œ5nVû7²û•¢9 +uîŸ<×0B¦Å@%õb‘ÎF³öY¤çê…O·÷ò½ ¿/Ò¿¨†I½Ü`"þù8”ΣšUÇÜÔÊûŽ.-9\¾¢Tl¥…UÅs»…=X¶Ÿæ'JO¢i<0uˆ²˜jL½a¸gw,;”ûf¬l[¥ÐÓnnýŽ€/WTÏÄ+tåœy'Î{ƒ5]"^ýÝÆ•ø³vÉYº.Ÿ•/]—;_¨ÇV-l:WÀãÊ5ß•+•ë¯Áýù¸)Èņ²¨rfî(œ‹œò^µgðÙ~a·ÙQa•¥‡î.gæøÆH¥ÎÜqc„¿I‡x«ç»e",®{• oD$Hgø1NpŽOÌÐl¬=êœï–KeÄxÌpÿÀŠŽöáƒCOÛÚ5üŠ|ãvA]âåŠgŽ¨eÒÃMÎá¾Ë1åk,D1{³_9ÿ‹?täihÆ ÝÔd +7-mvgy®'< ì¥Ž»ý®5W´ +A‰`š®{v?øêw^û–Stª2þ¦ê¼œrgCwò\¤8øÅÏþ¦„…ïŸw +üõé”5\„ªT¢È¬Ú_Þ6g´Qú §¢ieeßSq¶f†"T%µúµÞ±­8'~~x™Õ§–(HkáÐ*…£Kjòy;“]·Xèõk`œÏëy# ¾ÎJ²Ñ·hªPñ!xÒÙdð•*Å¢{5{Ùvÿ|f‰qï\¦çÕ;ÏæçÇ£;T~ÎŽ¹¹_¹;Ÿ×ÌάäÈ×Ôþ¶¼ªð½V†–Iij'ø‡0pÆüæÀ ± +,FÀæ'–»ƒ„‡×¤2Üÿ,µÿñ’¥–"×cyW%b7]¾*Oºmì°øE8ܵÞÜå„‹™çüE€[È`fÕoÐØ–¹xAeíRÉ,¨q¬¦ßpŸÍ‡jQ{;’©3Å +Thïë磊þ´Þ~ ¼üÑÓ­žo£ÈÊéY4¥>%Áùch¯ç¼<Ô›¾-íç“nôŽ»òô=ñ€î?×¼œ·kÞ¤ü»AøïﮨB l~ç½Ú5ÝUùYØú"èzÇi½òÚ‡*/aü¤Ü#ˆÛþF0©a„†ò£ƒeœØÛ]û´AíèsdY¨tÒšteK¢¼Gö ›?¶±ª*µT<J©êåÉÚ$ùõg šÇ-…Ùs¿}Á‡ì_pîFÏ¢i=ÿ¼ ɳ™ÁÛÂÞ°ŒÐ†S÷3>Üuõž!}AŽ«ùé{ÎxüZå|ÎÛUÜÕ?³U‰*‡ÊÎÿ+KBõZ HÊqÞ8™PÓ¿h™ÐÊY +H^æ{áRXS³‡M©Iðù3fÃÌ¥¸ÞÎ¥ˆ¯s®£oj×!‡`'¶'~.ãGká¶\¶z&ÓÆ;ŽæsÖâ< ¥ð_kbâåš7ÿ4gõQ×~Lû¯+A”8ÔŒdÄ`Ì8 ì’$–iq*}ùŸ~™?ݶç$ÂG« +ѶŠ·ˆÑi‹ÎðÎàÇDâòKÍ®â +m™!¡T +Y9°¯ +PMuþóŽÆ@wÚ»°‚C +‘Î1¨º§ß-ŒÍ.|RX¢Ž)A=`_ŸÍBéCÕâ }ìåÓ+¤Ý>®}hû Uïüø1ó\Ÿ}çÏÇEHZ —]ºij¶9öo¢R³L CáÕñ¡4-€à¡ƒoäpP%‹ ´[èc±l@=y +& +â%ã)9,Í–8a•yþœiýj~€iÖW×㨅³„ ++,$´„ÞQó^iœ”e=âç«Q‰±šDj×—8j çg7+v&ª€ñr +4(ù^8Â.Û@¨1¯M`CÍÐüHóEäÞƾqd·s¯c]Æ9Îï%(i]YÏ´Ë™ÖÙ4‚) bÇǦù•˜…©¨â™uÜÂpB˜ ‚¬ü$<—±¡ßüMí­ïá¿¿»òï·’ÍQŽ¥¥Œ2îÊRAH!]E¦íÕ5%‚÷ÂE7^„¤Ѭµ€—(WË>EÅæ­0”5K>e£¬í…¨XXÊ(,°užès]¿£=?¾ÀBó§^™*×W“™……¥Î XXJßø8EŸNxïðô‘4Š»²Ôèy$ë£þŠ¤Ä-þŸKU–:sÊR?ãóײůZHT#°5«^» 9¡¢Ý¨#ÁËBʪªQßqª*¶¥TO©y@«6BõâÔu¢íõý8ÉM“®ª„ßl}jU/4£45L¹Nß“õ‰ÏZAõ[ +üj/ßaz»¨öMÉM0…¥kŸX*qZ4ˆïÅÂXê—(±„› +q¯hsÐbn¤ê•†õB““àŽ Æ(“óÊÂ~v ~0_=/pl«Ü¢@cÍ\R¯ÔIŠµ Ê$7¨bk»H{ëà %~³Î\ôÁé›øIõB¾ +µÁƒÀê—b“Åú—•kZO•ÙF¦`Õ{³!BâÕB1…^µ^Ë&§‚üÉêûz¤«¸Ë‚o«-²ø$Vm±Û.ÄóeQ•W“õœ¥B½¡=9–HíªÉr±äµl«ö¯—V¯®Y›ÅA+/ŠÁµ#RÙì–WF7ÿÍ™×>ǒĘ Õáôe[᫼’s‡Éd&¸å¿©<¿n‚¥Íc“% 3öÊdæ^Ljgç¼R&ƒ…5-Í ²¿ä/V,°ÒFð*Ä0.VÔf‰J£x¥LNÀ‚³Æ·¨µõ¹÷ìB‡û‘­ºÄÒÜ«†¦ÜÍ&µ±çèG?ámÞQصKelUó¦bí.„Gr;¿dÕIõd)LÌêàtn¾À«çìŸp9-óÎ÷å¤âÆ¡µƒÂ’³oÊD$#E)øçL"nï?H|$gýƒKÈ`íîrƒÉIK›¢o‰Æ/¦,‹ÀŠÛ7eªv¨¥Ƥ¹dÓ,S"¥Ø2›ûöáBš`¯ðkí‘N›«Ï +æ ÅXhÙð¤dJ‹äËT%Єx´µUásè RèfúΤ6_ƒ´\kMH¦*ùç§9á8o× +^Í*ä”v_?öð÷¶Æ-ñ”íjotµæªÉ(ðW×\ã;"÷åUá}‡lhÛ¢U\øP½bü¢HæDë­íeìQ]Û”{ÅÅ1D +_„:+îZÒÕšsTÜ•8¶ñtuË„XWò9g¯.)1Ù”à–CíÄ':„íEÐÂ¥ivpA +m+€^TZ,ƒ[2¯ˆk¡t´ks£ÒÌ4ip4Ÿ¿×÷–«ŭØÜ@_…jÛD›U©U ÙG¤Žç®×)$Nxp'Ì!é3¤r2³’˜à2d¿u;£U* —’©Xí×ß›¬?ˆX›‰®¼Ä¢ëY?ÉZÁÃÛi»Œ9”Ñõ‚NŒÕ’ÿ_÷før{¥6±^!3b^²Ryeô_ܬúÐÓ[Tf¹â¨ë©-<|EYíµ"-,–°q*˜¦zxÖ7ÂÌÜ “s»Tf É±ÁûÁ)ün•œÐæa$x3»Œ<ÊûÆñ«ÝÃ2”ÃìÒö²îƒƒ‚´$±è±ÊW㶷Ø2ñ,h¡+r®,c3ôB° +Qq£Ô«·cfÓxŽè¢å–Ü6ÞÙ_°<½¿!ëh#Ã<$ÛqÆÈY‚9¡î˜µ½62Þ¬hý¥Wp¶ÌɃ¯p.`\B’îí’f^‰[2 ÊÜÏÅfa‘3¸ÛÉ#©q‚}¿e™Õ¹LÇÇ ¶¸äüÐ +±›ï‡"0ÕÐ@ù¿å¦kÉç`JÞµ$iìD^.I’D«\Ò«Ôõ'(¥¯ç,°óZD&˜¹—yÇKü‰‚sžlú¼ŠsŠ´¤rÔ­¼ÄŒ.Qk71¸O¸¹…5}0`˜ƒ–)ß ƒŸMüy­ E·µiß`[>/‚3¸SªG¦lE,·*U(Dd ƒnvÈòÉ‘÷u´Ãv!KåQxƒ×ÿlS½§”ÇôÅòÈþ‹Ñù +ø-l=³,sò–Gh©æ’TNý¨’¤ZÒ€[›{ª¡²“µ¨Â;¤¡>¬ã}ßêÃ-Á£?¼”·?±d2%Ði—³× w^Q ž;u…í&ÁÂM5XC†É‹X"ì’NÖc1õìç÷|ÎvÆÖÐ\:ì0íêzô‹®# ÷ò–¸÷½NoãÅœ‚•Ä\X?òj›é'b’dæJ*Ù²ÕÓ«’ÂoÑ)°ÿûe{Û–¯0“…Ø–¯ªDnžyœŠ)7…ýáÌ~Y0¢égq±ÜnãM w}‹Ãÿ‡±v’ð°Õ‘08²pàwŒýl£P3ÌfUâS9±‰ +ÑØ5±®]5tŒå³³d3Cñ¸k$¯+ÝZÛœ°Ö¶Ô\Ö†1£6‘/ŠM5Ö8±.^ÂÆ^hþóõ„Ÿ_x–"± Ú ûÕ'šÝD;<÷Ð:ÿ-´ûŒ‰áf¢,7;¥È‚KˆÒYS[áˆò6›/`y9qù‹ƒŒ«V¨/W$+\ÇT$Ì´x‰âd××'±ÊG‘Ì8ùAM”¡}ŠÄâtµ¾žpe‘]†ìÇP$( µ®nY¦qIé?572sŽK“(“ghe¼TIq§ŽTIéÒ=®J.‚Þ)R%öÒ5.A[•  Ô¥IÊÐÆ‘ªqH*™²Yô÷‰4Z®Jž³G•LÜÕ;ª„9¨*q…yTÉD~q÷0~é~ѪkEKì*Ò§{†‡ì[˜>ìx>®¬Êt¡·&)#Ê|¢¯æš„íçÇtói°ÅQ$ÅÍ¡HÊ%òTT;:¤ ùkC‡ ŽÅ::ÄøRž®®ns໣BP(_ +b†R:óÄA»Í+ê*d…!–™á{á(™ +ýôjò¦·›ÆÔ!?w6w;Î÷ÿß°¬%Œ)9ƒ"¯%NQ`ðJéu¼ÚEú|qx­!Œƒk +mC¯ +7{ãï4~qÉg ‰Ôä`_z>êÑWvª]&9[3» •óŠKìàe;ïUUSÃL¢²àÚÚ¨ÈF +Ž£µË½P±AÔ~qf’–wË»”qts©†qŒdÉ=c%Esñz…ÏÖù/F?΄³Ÿï%ùüu™Œ ªÖ“†·-]Óš»)~(Sv.(–<Ë)HH Ð's*¯ÆœGßcª¦ñV¶ñVãÔêÛ7ÉU–e\, +¡k5–"F˜b¨‹I5–Òz[·&Ó.•àëBã-%d·«èIºÚ¥é$°…ý©ì>7ÖB ºvÖ+¤îèŒAZ›«ECÃÛÆÎ>øKMլʴ ÝÅ„Qp=ß¾1øD¯vƒ¿7€cÎ:691m½;“?Ût¤¥µÅ2Q°*\X¤E_J#ƒ„ êg—M +¬ ŸõrÆ<”†MI^Á}m\ªßâ +~꣖ښڦ߽҄á*õRÒskö‚‰‘Dà ¡C<Ò™&¸óhfm\éë]1*£pŽ;Mi0Ó  ªhi›bVÚ•¼3È+œ‰ÊëýK,éF"_€-&ï§ÛˆYËò.qG°>$ØiÛ,ù‚ÅbTy>/^›¼ƒcóP×µÀ/xþ_˜XLÛyi§°0â׋‚zŒßm +õÎr—Ö¶D¶F¨b %)Й}u© ´­Ã@³‰Ñ37Š/Ü2iÂvöáG¦¶ÊÔŽ¥c‹k¿ÑÞgƒ~ŸLH<¯kŽ‡÷#H…ЕínmÞѽXrKB7“3Â`¯Ìl[;¶øÚþ ¤Ã³‘™‘»6³³!-„.'Å<”ŸÖ æƒ ùnã2¨Í¤9ð­ +YØ`?”PÖÞÌço_ÃË%ç¥6{Žüv[¹Ç9tﮣ÷Æ[1ÖÖÐÈÆÚ’wöYþ—õt><ÌP*Nê?YTX°)ëS"ms’ùçB_V¥ëËØ€›…"L¬.rÛ§M^Ñ;üÞ%IpÕü©7J²,fÌ練¬f'R×d½Pëeáb.+‚ÁÏ¢†Ç²²Å3Xm½r¼&¹©K4à +37î®/Ýi6Ù$Yä_ëÿ~ÙÃßûzW}»Ce~ïë‹bþK´µ‰Ú¯ñ)ÖÞÞfþVvÝßæîÌ 6»AA%°«D~g1¥­:H¡„‹E\6\oN“` +q즡m®kSŽ„0ÞÕF·t¡z+E«Kç]˜Zi— Œvf +å.ÀúÎ,Vb¶€rA˜¾ú…³“µÚgôÒh bhÖìÂ@oË®ÈG`´ìª~†YóM¡PA®k¯î˜á1óVŸFÉ÷„zkX€iÓM4¹ßþ¾û¹W†ðÃqâ›@ãÒ¯lKÌ@Á…­›0ÁëзaG釭¦ìÐu€lÇ™˜Ú$eC¿ÎDxÓâ49øAyJ|ÀKc®X)àî>B]ïiÒJþ„‹‘e÷ú|’$ã,—ª^BÄp—ù2‰ÐVFÐ#,f‘Tè¸2¤×ut¶ö¦ Ò屬 +trÎYÛ=aPW›ó~(‰-ðN×BSÝ’š?Ãhrû[j‰3tiº˜¬Ð…Àè]¸h³£ãÃûRÍåZ¾‚·TªkZ2PY¦_@¦;—ª}È‘ÓyÕ" ̸eäQM¤ƒmä$üÉY~¢ÊùéÜVã,ÈùªÇ±¸†ßáágtMD³0åYw¨ŠâW­Æ}ô=~âÆȼeµî@.ÝôÛµ…›:Kq`\:°ÕjGŠ^«äsØ™t[Gek») r´ï +ïD©( }½Üô,ŽèV—Kjèî]—Ÿº¹ÕåªqÝ¢»…¬Óê§Hä‚c›Æî^ðšÝ™º‘ÑBäM&èôÁÏÀæ‡= ¨±¬“fÁ…ÿLMyR‚«/C榚¢yJÞˆò\¤¢òùÙo” +¼Àù¼(úù³O´,2”ŽŸ>n +´ÞÁùW³@áqâ%¼V… F\¹ Mo.µPã9"'±ö˜t>ä@n–ÍìÁ0£ ©tW^ »wÐý°Ã» :%Ú'gUfÓÒ“àtªI\UÙ±[ˆF÷sÈ‹òÜ*Ønc\N°7E·³U!V7XÝ'ÓöÁ• +?L¸²¡puâ +x2.)æDîÐtÊÞlð04#†žv6*orâ '¾ª +÷&¾+èSüîî Ð\åÇE{»B«»µO·¹-–»M÷ÅM±ø^!ØÓ²À¾’„åÒúòŽwÊâPªPg™m‡”Õ¸·´ªPh|ÄÝ›°Á¢Âð†Û~#Æaj‰ó™„¦KTX;ú½uéqK…m˜ÅdŽ…ð¸û%¿¾8Aöš¯k: =t| +´Ç‘9¾N¸û,wêfÎdÄÔvIÙ&ïÚöšï.tZZºj¢>e^_)äù­ÃíqÁaî‡ØH_!ñZ.–c¿¥Šƒ~ÊÁÊ"^&ZŠ~˜ÍÀËDN]míÆEÀ¨ø1a¿ª¾ €Öûãå…Ÿæë\5ܦ³Ê¦ÁUñÚŒ7Y;¦”kÖ¯õêÒ°Z/WŽ±^ªëë¥ê@XLd~®¯î˜RäqVEöR™Ð|ls˹0ñG˜÷ ¯ØŠÖÝqp`–˜Âö¼|ÕŸ#%NÀ—hßd¿St<üÝn4(>á,ÿ§<®œÈ\AΤ¢šl&HñŠçYŒÅƒ­vž×ÕÁ8Êã–jéá×äÀz-}*Œ +xâž%ù ‹rîë,«%Únò6EÑT ‰Ý¶=ÙZþ„QŸA4ä%pm?ìeŸï&ïwÕ'&]ì#Í« C7%ÐÍÛGoãp›^·†Ü <<R^4aHã_¥…—^çzK]+ˆ¡{µÏÅ8MFĈùmDÈÌ2i ^ÀÛ²t›Ñv¬bä”þlÄØ~ìyʆ&õÇ(èŸC™L…SZ@ÚbϧïÉ;ÊT…±(èG¹Ð%µ"äXX˜+âMM׿ŠÌÅØÕ,¼fCgžÛF#<@Š¤ð†™Nw?!re'D{8ë8Þ­»8çEXqƒÉá0H)€$« +Kèt¹ââºÐ7ó,lùN12­€”VïrŸnIåKL +ZPŸìUrÂö+f<¹ +Vm…PÑýŸÓâóyŽ[‡7 ûÍ´¶èµ…¤ç¢ÓPXp´ßŒ„’?9ëwnÓ•$yûdÑ_8j‹¼H‹>mkî×Éßò ²û ù¦@(1óe…=Á󯥌\åü}@É~þÁgÜ?a +º^ØádOÛím˜Ï+¦,ÿÈ„ÕøyõƒˆæÓÍÉ"ãg ÓÃ81RR4×+|²ÎGl6Ÿ«g¶ÙÍ÷zøU2G°ô¸$êÚÈZ£&nXá'–rŽ%Ò;:Þ%ÖÁ ¦þZ$Âgox® Œø {/vDžÙÉÓv±¼ì€wkÏ+´Ì¸OëµF®[½—Э÷eëC•&ÖÈ_q&Ë?âk¤^œÉf'ßËñûý 6^R§æ ýóq(:tÖô§ƒê!ݸ*mù@‹.r€7jŽ0.êå°Ê—_Ǫyœ¿; næxpK¿—‡\·­Ð ÓjvÙJ®䵋v8sI¡æ„ȵWÆm“UK!¯Zxªè£_pÀ’Å8bÉl õ`qm&zRÙ ÎÓ +Íï#Ë +xyþ¸ç²kÁ›Oé5¸?7eè³²v¸Zaì8‰ʲ¹&×ÐÃ¥mó4¨fg½;]±6Ò¤à¥]Ú0BN:®:©Ñ®9.ˆà¨uMŸßž,oX;ÃE˜À¡äs“oÞžˆZt¾rë_ç§0Dã×åAÁu„–iˆoxð-e]†¤bhmW-2 *`‹¾üÆV@\…ܯ=S‡Ü<Ôž'öñl™³[ünq÷+;` +…œÖ¸S8ÿqh ¦™J5úƒ¯~ç5»ëÛe4Ñ›õ‡7q±øáñº(òŠÕþné=mñøo¹~â³Wèñ¯®0ï­IVÕë`Ú[sŒFkÔ»Õƒ>™á³–àJÈKöM°›v¼G;W­S˜Eh‚Bd,)\­ u‰íCÂË¡ö7îù—è˜QÖ¡‚ç e2d—Xj¬Èã”TJZmÁcf€b˜|`5âpQ^£ý"ªsFPže3§MgjíÕžÃ1T¤,â›ð +ãDSi)@T˜Ž,ÀÃj +9ŒÁ$ƒ »NpLEIG>…mɯG°!û5[Ç_þbg=Á‚@uŒ`ÕÞ°ü»¹–?ƒ`rk-‘1G˜k×.1Ûô`àÀ,A@ǶÖ>ý²4Ÿ­àÐÜݘâMl%<ð4œ~MN?YšÍeaF“X'“‹ü?ñÑNq6PÙ(“HRÔì +•¹EÀO¢…ÊœÞ, œ$¶Ä9pU&n`¥»‹§¬xÛ‡qűWœ[Ý̳¶%÷­ìè­Š©Úc\œ4toc[l»{x¿,!&»5ö þ8ø…)+œýÚCi +(â¨àÓ¸–˜¡>eŒ“„Ù(²0ß,F‚íåy™áÑ}Q–ïæ8fflŸÁü¹ÎÈZl¦aæfØbm8p•‡Y„Â),³S<3^š˜¦¾!ë–~¡TÌàKËê®D˶àîx{Âñùù¢©3Øq;üæ“‹ùÌ+s1C\T̉ã®.Š“§ +F*æóˆÊŽZ»2Ó2g‚8/á ÌÀ* nx.fˆaä›÷E,´ïl»˜|¥bÁ31cûÃÄwìØÓÊÄŒ$ŒêF›Õ_ÇaŒ…Øêç!  O&f0íÆNàr$‘ÏÎ_+±p•hbÂcåav[A9‘³¤]Õ’À‡6õìÅœaÀFOfìêT^m—íNY(P䉘!9:qItÉØdÔÈÝœøÈÈL¼ÜuÐT?äA¹¡È4®=‘J>‰˜!Òʸ†¶aä}^ü¶#Oõšù^däaVûWƈ,ÌØHsŸ,Ì6þ¶"Óc3 3v‰9œ< 3„^÷ôµ”ƒ'Vea¦Ø#$iú̈{Ócf¬ãÄF;¯ÿ\½ehðK +Ã… ’0ß›uÉR:À"ó0Ýq–äC239vŸÌèff²`îð¹âOšFÞ'ówñŠ¾mg•éæ/\!Ÿ̘Kþœ‚sýyQ6¯vŸìʾ„7·ì~PCŽ ˜ÝŠŠÌgìÊ¿ Ë hƒ¤u%<rÚ$¢üË¢\ù—Ýz=ù—ßì;›3¤ + +´‹ç_¦ŒõÌØyQNÏ¥]ŽUéù—•éù—E9ù—ñŠ|0ßF30ã³™Ù'ØJŽÌ*ú¹õ‹ž¹òf (³s¬'`–ž×œ™tÝ„”`Y æí*Iä_éÿþÞ¿‘Eµî„I/ŸR0SÏ '£Y …¸†LÙ1Jc9˜³J*)3šÃ¡4”ö,,ã9˜Ñ&¨£HäMÅÊÁ ‹+1¯³å`F{2]2¥HHbÏÁl$øQ”ƒ9D³r0C4§H¨Ü)¼™L:‡eØ!g/‚Ö>(J¥§ÌÖF\9˜aßpSzæ¬ +Zž„9Ìea¾ ¥a>OÔ÷«õÇñ>|åvxàñeGÖ^?¥,fpu#{ÿ.~I­Ÿ<Ì´ÌùŽ®3ò *óe˜+3(k°_pËKË0 +sèea¾þÚdûAÄ* 3¾¿ú„$e÷ÎZBÀó¯¶ šC±º>‘…™–gŸü¼üL£4Ìࡶhu/ï&bûÆ6mÞY˜3Ê*õÏÂŒvázå䜧‰B$±éY˜Á«…èÀNîÖ PY˜³*Uyfì¼Îc§t¹È5Ä4ËY…Ë¢9\TXLY˜¥vÄsAI˜¡•X +Ry¥ý$bbäù<˜3={¸,â–/Ér´„°Â¥ 1¥Ì!¾™ƒù’ÞÊÁ|?À㨿áòxÌ}ç`F7 +³¹wù.ÖæOª¤»ù2n11 ¾Nf™$J–¥’0_‰’0‡E¢$Ì27NæË"Qæ°H”„™'»áb˜ ±,Ìè–@’õÊš}Iþ+ óQ‘†99>ÿof¬tkWf.4Á-$Zh¥a¾ÚÓ0Ç:) +óYG¦a¾WzÇKü žYq::y˜¹®žñ˜jGýRfô|0‰oûœ3³T<3f°ó:ôÑsày˜±Ë&?A;Gûò&8“;e‰wdÍÂáäi˜Ã)Ò0S–ïOÃ|´Ãv)+õ¡DÌñ€21S} Né ‚2ÇüÅÏî%%bæ šø&?AK?3óK(3ý,\vn,ö¤ŸúC‰˜þ`&æ£!Ü" Â4Ìñ÷%ûв'+óóžm>2is+ 3¤äâO܈v£€Y˜ašG#wI#çÑ'8üèžÏá öáY˜xØî :{uk}7»“Êw;Ó0_¼©4ÌGdIJJ¦”cÆ‹uþår‰i˜£[LÃü„`»Ó0Ÿí}Ò0ƒËW¿Ò0󰳯4Ìo +ûé½Ò0_§¥a¾)á+°o4Ì¡G< +3DÁ$胎³îGl¥a6JV¢æB¡fž7Oà !'­ 4ÌF"*”i˜­M|£b5çâ–qÝgf´+p´fØæ/òEñËhâ!{¸ÿçë yç•…”Æn& +ÈŸ"a›EæûjdJRfŸþP ›ºÂ祪!,eb~ óv›/a —QEþâ¡“ˆùV%K¿tU2å vU25ëçëJÄ|TÉŒ# #àC“ª¤Œx‰?៪¤ ‘¡J”‰ùô‹™˜C•(ó½]•‰9t‰21‡.Q&æK™÷êH™(ÍòQ&ÁGï)ebvmÂDÌ—2Q"æÐ%eh븪qNpe2e¹øÌÜ{)_’P&LÅ|” S1eâ:ó(¦b¾uÉ Ou S1]2CMú|Ïð‘I—L9h¨+”‰ùè’2⼌¿wñµt‰ÚÏ{²ùˆt ó0‡*)îfUR†«‘¦a~Ž)ºVs´H +ÒµH**Äõ•Qg %+¡DÊ“OJD‰˜o®t'Áy‚ØöP"ó¨Œây²Ý S•Œ{­yuëøóP¸èNÄüÖ"K‡ÖÿkZÖÖ”üA+³ów•ô +þö<ÌÁàÊÃì«ü­ƒ+óyB0Õ?ž‹ù㊵գ³lT.føV‰dÌW8 +ɘÙ+§S2f¸x‘å·‡ŸA[BɘãPªLË8A¢lU‚=/ŠçkæyKîF%cÆRŸô-šm3»cLVˆN%c¾Ÿ ŸÊE'Ó1ߢSÙ˜Ct*sˆNec¾˜”Ù˜/Ñ©lÌ!:•ùÕý'Qtþ`­; ï›ÝœÂ)+aÖuÁ-Î÷ḻϕ9ö¹²1_û¼Ö™bŸ×êîlº·tŒE¤cŽ]®ṯ‡c•Î*3HuD²et¢Í«}˜çPö‘ŽY.Ž“Ž™ Y‚{ÈÄ}D:fÐÙ˜ÁâÝ{Eh•mÞɘ)õw$c–ÿG¿p!?f$c–Ä~î½\cfµU3åëYùãçzÿÎõ(8å™÷a1òÖHf믦¤ ˜–5?@æ5{¨"óHnþ’Þ &c‰¢òƶ[sáM&‘LÓ’$J7{Ú¼¡ÊP…ù#Ç;rx»í gkà©Àp‚0\ ­¶òšœ˜ÆD¨F‹ÆªT¶®æ›iýä1‡Æy»jô¢X¯”²×^a‚eëÖI&a÷ #˜/NfNÀG07áÆ—"÷kì0é³Ç2Ŷµg½f«ÈU)¸Š{µÌØTøQ ºr¿½Stæ9Ü1™¸À¦¶lÊæð¢Ô-è™í,83™) +òÎXL¡¬¥1aQ×&ß9„ÈaGŠ‹È 4„)È=ÐBFYži \Ø¡µÏw™¶ˆ/x·…H„À)˜uZ Tã §XÅëD 2ïPDÆ1UÔÚš/;#ýy 83¸Ú‡ƒ…I Z3™ Ãp4¾§õm&ÏšB†*ž}gŠ­½#²‚‹ó ;chU†Òâ`nŠË¯$ÿ¼’ÌšUµ’mu-"ç÷Þ '1ÕŸ“¿ØÌ)Ê]”y˜Ð@[¢Îrbèy)Bwnžžc(Rì&0Ù¸&˜¢ÐcË̈;C»ÇÃ{ﻡ(øEì†OP`èØÛIäôFöÌbÞ®BÅJ!¨m(± |ª³×vºRUñô%¡&ŒÉtyL–é4úë¾4CÙ^¡±©xJÒ}j£(3ª¦à£ƒY)¸ŠY’ÿ݆½Ã%À•…ÏR,Ê \#¾@¸Û=ÙÓi”› +9Û.á vý$iã;:Ê)È2 R±ÌQ60´uU.EÄá¼2vïbîÄ$¾,vˆâÀE”Gzì>"³×¾š c@á@_9ÎsaÖð“6z®¯¦KùCY›8ÄÒ²'ÍéºÁ—[Etpƒë›„6Ä<ƒ}Øt°²—çæIrtüǽñv +¬GØYŽð1õZæ® ¡¿sàg[2M% !¥:óPX +[yˆ³J~ZÈ[ÂÐqK’ ðD“”vÒͦU¨Ë cÇÊ-U-3 VhqvíÁ,]ºéÅÚï™Y¸y>¾v­×@;á +¬‹Ý$%7O'F™ª‹ +4N.L &6¤_½Ð!ÇÈ>ñrXc^dºÜ2ò]ÝšªA Ç” é,·6bØ^ø¾¶( +ÃJ$Üê"hù/ +g'Ó—™»Ö%Ì… €@h²ï…/@Æ­äûÆs±\O ?wL¥MYü…-1f +Æ峟\2sÛ¸Ñ퀄Ƭ)Òñ_|GwK˸կT¯èû1ÔÁ ²º³ #ð&ºÆ^ú ßöõ€@ÐXŠ½MOzÀst)*—µkq·ùj0ŒS<ìí–}[4þ½ ƒ<ß„B)2Ã[t3ôù;°B˜yΰDÈhãÅ ÉòŽ…PÆ0 ,´Š4Ã*qd‚[eÓ|U'·I'º‚¼UiB䦰˵њäUrˆ¾6ëowSózS‚Z~E‘Kç›ð;dݧ¶Ý¤¬I@ÏØFèB„Ø9ï€6®!©©YEƒ±/ sT†’B +7N-EIò,Ìñ‘äY—Ñ≢³j&&<¡®¯¨Ð•QÐõ‚ÚkkO‰ú•_ò„4Y9V®pWœY›ö¨&T6JÖ](·Qp6¬Ç(³E›Ó&íä"ÉnÎÝ%2?ªO®a({5m—òð'ýÐÔxë"5HK”ÿ@5RðÊ!xIf?²Y‚ú…>q[ÁõÉ +1âúd tÀ‘ôÉ!¸>9ê“Eÿzè“%ŸÝÑ'ûXñÔ'›P¿ÐÊh}ôIQl–`Þú¤¸G.JQ&\W(ÅÍ>)”’BïI¡7C¡8DÇŠa5Z= +űG¡€n…b=—^£ Þ®ø\¡­{ý½IãPŸl•L»ôÉÖ7\ŸlúsCŸœ¶Ë‰C¡>ѵœÐ'ª@sé“-žw}¢;6¡O…}é“-Mèúdîçú¤¤cÍD)tÊD×'¶Š¥}R’û§¤O š‘{èUÈ}bkZ?žk£N£\êÄÚó¨“Ó”:9ªÇy¸: ® uRÒ9¡-%jG›”põ…6)º¥éÚ¼Û6 ùr¾øé)´‰Òh‡®`]§K™lÙ1çžE\™XHí‡2Yríå@q!ðÊd‰ÙC™¬Ú¡Lûáºd¹Ã–ªdÇdº*QBäP%Vλݪäø›­~©Û`mUBaüR%¶Mu|õJö’ïMKtÉf¨úº|r)EÓÎñ4(Í/ +œäoá×s‘ÎÏ¡ù•b.æ¯×¯@¹_TDüzQ쪿£Rl†Š¦K‘y »¨z!ŽÇønš—‹ÝAí¥É‹È牶ƒýºûwŽՙ‚€ž£¤³pIn“ø–j® ²óK +†GÂ^ÒÔ΄.u±wô“AÔ=f¿h‚hGEÏž1¬Ò¾Ÿ(aÃYi!¦Ý“aèX…X=² PT°ù´]À¦AB›¯›³¸ØÿýÂmÿ>ÌÃôrp;Xæ8¸ƒnÀ½.ƒ/ýrp]”ƒ_)JÍ¥2#¹ƒÛB®évp;æâRØS¡)bz/àà.¬v9¸‹®D¹ƒÛ"ø:ÈŽ~šáÞv½Û†¶Øëòn[hº¿¼Ûº\ÞmyÅ …Ÿòn#:ß®G|âèݶ¹N/ïvQ‰B÷nzlÝÞír\èáÝvpÑñnXuy· Ð9v(Uy·K÷ȼ۶ê2Óô ÷‡»bìaÖ˹ÍÔ?Ù]ÛE÷î³\ÛÀ’ÕØÆ|ÅVH7{-w]’)Jy{¶‹G1Î#s%ðJ’Õ‹+…·g›@¶÷l_M‡­ +ŒÜ¢\ÍîÙÆU ·g7t>±Ln—cP¹Ë± +èÈíØÆU3þb ƒâ»x¦&7i“g'»¬>òÀÇö_Œq¡ƒþÿiÆ!¸W0ot¾õ ƒ6vxPD<¶ò)Ó»¹õäh +Áƒâ‰æûùûtbÀ’ýð Ì£Ž:RGeÕ¾ptŠ¿:(7gG¹ëгHó~^AA)‚áäžoGáä}•j—%µöº´HÌUèG]O±rƒ`ˆÅ‚è¬.pîmœRí—´ pöw€ƒl kp<éŸdÇÚtJµË˜~>~rÖ òâ6§pÆ‚ à”jÇgÊvëì+à«27×<¥ÚaÖè'5\ëuG©ögqp@Ÿô0äVL,Ò™@ ø£qld­ŽöáCÁ…Gñ@tƒÀ9˜G±uŠµCô´S¬G¹û”U#ÆñA¹‰cÛ9“æ«X{œ(D±ò|Ü»9Ga{7Rrך*äUÿ2©gÀ•4qîh2 C4“î4£à᳧*Ð×úÝdø +Öí6yßõ´©4*±HÇžHŽØ>ÛA1Â÷Áj[©SÀ6ìĺ•ï,Ñ{¶YÑñóÃqDU€1—ÌV¦Áij¦šŸ.˜mOPxs_ØFàà¼zŒ †½ŽaÎr­‚æÖx‘¦ +¾a¬a @Œ™:ÄË™J7Ka†¸7l\*½ÀÛ† óõήFW®x¬ñzn&šðCN"¡TFÈ37°9KëFŠms_8hË ü÷xI<[:eü1ûMô*s¡…ï2Se³¾ú§lÆkty¢"Y‰ï£2X}u(!§ØÄt êc}(Cgš¥øELµ>ugíÊ ›µ§ +G|¶­ +Œ¼xÄ’%O~B€ Æ/ô¥ã2ßá«™íBµ­º^`22øå"à*†°`[¹#UxN{õól–…½ +Z\W$ð±f +“9®öŠ·Û‘`;7ñÚ“µ“W|2¨­Y%2Í ±ž±ÖŸ/>Á>µâBBÆ.rŠ!ØœPë›0áR·÷o¢3p5æÌD(8³2<¯$ùXÏr ý9Ø ðÛŽ XÛ;×o<”Czx$”Å_lŠƒ¥EÜ•ÍÂPyoüª}!Ã3WÄt†6Õÿ +Ô—O§5ÍËfr¨ +êÁ™¯ÍZ¼Å!' =< +ÈÚ‹°›º‘™Æ¬K{{挚1cÍ­’2«>X–Ø\@]zñ*`DÁ2Jºµ„°Âê§Ø²HwÃ9#è°ÑD˜l]ÌPîà3(›•GÄ +‰,[5RóÛ´©TО2žPÇD[•md+ïg¹›­G;ÃgòÁ·š´52É‚D*¾D˜ÕE3 1w¬mÕ.xÏàNvÍ\›¦1/!Žs+ wÓßØJÐ!;»˜½ƒÿצ³9o.a ‚ÙÌýøx°DÁ8[ê ÒP>¬¾õ^ößYAÅ„ºö Rñb‚¬ Ƀö¢?ML&« +6‹BŽ™°]n¢Ì“˜t¬;÷nHN#B8LôK°Ñ;.óçüÑ|\`âRdNˆß ORÉ2‰éDÌwº=oC\ɺéxšÆA2%[d)EKØD²±ïÿ'V¸Þž €ÉÅ4§ŸAðUámožQìö®ž§L ¶ 4×kĬ +»§9™§ä­4Ö™È\“=ÕÛIÂH=HÎfQ¸ ŽäøÉ\Ȭi“O“Ó`_û`ó%`ÛqPv –Týmnÿ=-À +?¾5‡Ò®¨ÿÙ³¬ àöžªéÉL}{2!´A{*~>–Cy +Ö•”⇣ Ñ3«d>/–iŽx˘~Ïú‚æM‚VÔQÙ?·#c¬•ÓÜ©Œ¹[­ÉYçäæß]˃þ4]V”òÙßçZff´Ø÷{Žš1 + Ï-Ô ýp#W&¼A¾º>Ãr††³“§mÐMLxæUÍ#Lé€9Í¿ÑáÂÀàœ=nßø8®„ÀI=3ëÝÔƒ 徉ÌÀÙÝô¿@?SFxéÞÄœ0ã –—6†Ê8vYU‰Üèpà…›íŽ²¶}j X5õ—™êbê¬Pù œ]öïút*aâo\z¯4TíGZ…ȬvÌ^_÷’e&G³«"¹žKG`­sy=Re)d¾à6ûbì+úsåêŠjDZ.Puì’Ž;ÈTVŽcÞÇ]üÜcÈ"ÈNÀÞÑEÈÚýs£…püSÅ3 vJÏ¥‚ØßÈ«¤IZxÛ<Ål#œ ¡\ß:a3ÚÁÁ¸}5­#~EoëøÞKBH‰X¯`/iõÓ@ÚUùP`ÎQW:“œ<<¤ +'xX"Ö™ 5½Á¡ šqa…Àge°†{º&wu¹ñÜ¿°O9YlUS;R¹Ä1\YÓ ƒpâçáPˆD<ƒâÆ~..m‰4eŠz ž[,ˆ¥/½ „é`[{·ùl[Ó–áôÈxÂr8ÄDÎD3˜Èɇ'ÁÝ.M¿'}ù‰q}ˆ•óFÒ¹h.éM Ín2¾“4(DÍx²^fZ‘;g»mÑF²šE û8¡ÆÖzªHèy+S!´ú"T?aØ¢* +:kheFØ`½lwûXxM2 6'ÈD>x“v8„º>”`F[ÑïËÚGÌVÏþ‘¥LLx"Ýèsp»Û8 œ)yZO ×­ŒöZcŸéó‚óî€Çú!Q+{û\Z“)ãwÄÅê +HÂ>¯+ª¦ƒ*PÈ\O* +dèͪ /:3ËO˜_r{I©hj“‹ðøÉï—òz7EznG.Æ£½ôqñóVèc ÿ\êØâEI>EGáñ·Õ‘O‰cÜm†êÒ7ù“®ŸX9.þDß`FŠÉ-P”ºé¯ã—Lø¢”}HUè=oÉ—ôyµç˜¬e¨ÓºGk©´äÓ+ÌÎ)Â×MȪ]ô-CŸÍä˜Qz‹tž7]¡¿”÷v7ÒcS€Í¨cÑtž~XiºBà^4ñk8ŸÈ¼´43üÈYâ‹-Æ §Î盓HëèÕ`P'z=„°¿÷ +öÄ3ÿ¿VQÐIûõŠfÂüš'š)§·G3Ó;¢™¾u<šé»Ñãg»z43ž8:g^ÁÌãÂö`¦¬ãfÂoÙN4SÎÝ+š ³tžh&.ëD3¹¯`¦™ŠTÚŽD¦øÓ–`sÂ"ÊL‘L÷(¹§³…Ç1·JÞrÌ.Ð5GGÞ+ˆyýÝb˜.ðÂ|$ñ}øO0¨ f‰ø¥o:/Cà3tùÐA‘Kz>NàÒ~ÙÇdÝnÏ÷¼œPÕ‹§DàÜ0d‰ëÕ°´×óÀÇx%„Šœ*\¹UNÑJëm÷xoæB0ŠX¥Û„ŒTÊÉ#–S ’òç,‚'#èGK±C$¶~D0òb‡ 0„G-ç§M‚»zðƒ¶0Œ{MšGõN]`›¼CCnZ8<4 •“Ÿ›<§3Å&ýøæ±I—#±5ãüÇФ$Ë<¡H;¿òñcm]{àc&ðúŸ‹ðmäxkؽ¶»¥¬ö‹Qı•o~Yœ±ZÛ1h~K¤±…\øf^¨b¾@°ëQƒiGE0—ÿ‹b^ÆSYÓªõÚ',oþbIʱ•£¤¬„Z€ƒfåcm+%86K:,¦"VóûùÉBšþ÷¹±í}ßBÇšðòù„/s!GÀ÷lÌ1šÙ|ßoÁo¿%дë„ßÆÚ!œÅKXLæožÅ´­³š `(ßcR“cEô-`kZo47Óo–¡,üÝ0íÖäÛM)cžM¨ÛÇçÐãæMšÌöuxà ÿazM­X7úÔ‚€. +,‡¹уŒéÅ1—«ù-AŒÁ:»l>â™øõ$!ÓƒP”?ø.ßTð°÷ÙBMbü{Ó¯c¢?ߢb2í¸¿ÌaOÔäC&]X%«Iø…-×âú,ŽõxÖϘé{ð“ð5Ÿ›=ô@BŠ4'öžE»ð¶oy¢½©×7p›¹®Ÿ`—Äõœ¼K1“ð$è'ß_¿ÉûH’±…ðæ=®ùŒl±¾¥F¿óYÊ×Rþ¶ú&Px¡EüÞš»â«¶ÄÔ¯‡`ÝD³S÷t´ôO|Taq<¶qh4)&ÒÕØç*_Fä$Þ¤l^h5ƒ¥U4—ö¹dæ6)brÍôÓ¶ÛÛ +ö† y¹yZ˜1B+ÖÎÅç.¸/8³äÚ΀…MÞ(°&l¯üú•ÝÓÚ’@C‘±˜¦Ô{fšÓ Œá}1Ýb3m¦Ï6”ý1!vUß„ØÌÎs=0d©›Á²~™5Ò1–e&Æ÷Aê±v–¡j` eÍ“mŠ¡.iÖ/¾`ÈÎå:íL™:_5 WÊ8,3”Äóf‘W6œµŽÀ²vÙ¹x€UI­Y8[vJ³ÏqŸÙÂòõÅ5P­Õ›Tgñ@JY¿®´Ÿ8×àQݦÃDZ°õÆbR³ê;þÃÌ +ϲ¨ZEÎILx›J¶©n[å]Mã¸Aæ‡q“‘(“j¢:G}ÀFs?ã’†5Á4‰¥þì ŵï•c[kòFÉ=}šn‡`¢†û m°ÐÍpª4&ì–}Ju2w Ó¢ó^wŽÕqMœL‹6ñ¹)Oˆ®”f§.)„qQM´ÌâYñÔÌ ç|Þ”!¤u+(¬ +—yJÉ‘[˾‰ë2ü<62ÓyeßÆyœç³pþë¡ê¶»!ÁsKŽoÞℤ­“ sÊ¿¼©‚¨;‚ÒçØ} 2WĆ-ÕjÎm´&‹ºj/*Å5vN®´`U•ó‹+îûÞÌË—±Á9úCs=Úغ²Y‚Ð`-r*#¤øZ¦QÎ?9áê?úÛ¨Qô9Ù49*xš jIY%q¬ÊrÚdòÍ;&{uª8²Ä‚ðQ¶´­ySÛÜxóz ¿:¾ÆӆѼþn|ûŒ¸vȽP¯^—vòê1š?WâXµN•[ +oÖÀ5NÏ]m;Øx–› - u­ü9ó—&ÿ¯rЦñ„—o³ÎÝ<©:iûÉ/`³ºie^Gi´Žúf³º%é \è©äïL—íÌívšÚxF‘Ç2R×Ú0÷\Çó,Ëö6„Ü<{Ÿý¥‘”´¦®dn÷“¶¶h»S¼15Ç`m°l^¨4¹Õè’ëM]^†Ä±ý ŒµÉ[YÒÃT¶A¤8\Õ…0Á.7<_‡R\w“¦{DË2ÁÒ¡éWv×e +øêRT‹ŒC3RmØd%þ]õo-þ0ÓUÉw,`àËó +–œ +¬Àí½+÷Áý|ÙstŒÀfXîï©ûN¢¸Ë>¸â~X~x»ÝÃkG +Ì +¥wíÔã¼%ÑÄzËËónuº8'QqÒÈPRÔï"ð‚ÀÍÏó~¹&Žûsøçâ<Œ+.¥mѶPXÇoêDÜ‹Xó*Œ +Å}À:ô{{zmÖVSܼ´Û±GÑ|nîÐI2—uì¦ßrbŠÅˆû€@¡†¸OpKÒzrq¦¾Dz¥ÉÉ\òL]7"êƒLÄöecÙh¼|¯uþüméu5….k› ÞLq—ÁäÕ“^|=Ï/ÏÂ9þ‰PØH»É·»¥1ÀQkò´”Yên; T¿G&”nJ»¢£ ï›žTTeˆWƒI‘c(™þôXä!Æm…—Óæô†”¸I“Ö.«ŒñµÇq¡¡×{ËË<ŸQÌ‹ùP·àÓï'ÜkbË´yŠÿ?ÇNàrS¥Ã 8B$‚;rŒ8ÝKŒÞ»+†ÜC>6@´O`NÈùÞÎß%ªŠãYè¢]éŠ Õýd53²NÉÖDü3¶q¶t!rS5C93·³Ÿ+óðmJç¥`ËÑõdÍÂÁ#/ÃifjÔ $%âXѱ…>îyýt^™3V÷eûÜý„Ìs¡ž0“<5m¥%ì!_3O¯†W{¤fn**§ûÉ—(çîIfb’L‚dÁ³åÚº²jV,œ1-èúúÁ6ž€°Ò‹çœTýàätÈž*V)uÚ=M¢ hsSWU´…ñŸ¢´¬NU‘ÇVv€ãÅ× éÈsN}Êlé. Âi·@úœ¥ðÙ²²p~†¬ÈÜÁÙ!˜½û¡ÌÙ‰— 霞p¬âÕ™‡Œƒont(RS&“M¸ä‹K‚LP°”Í@®båýÜBD#Éu4TØURŠ;›o)åÊ•q ykbá”ýæ½°¿®µ ’ B qñ¶ .‚ÊöåIYñƒð½lSAGÿÍo„‚²7ÿýÝæ¹ÜÇgó@ò´ŽÎ+YA+»'RòÛ³„¼;3ã +Ú¤„Yg·z=%QþgguVr9Sð•ÿmÞ›P=>÷DY“zŒwš²¿£„ ²…¹7Hx8”¡²0gÁdÖ./ÄgÂ]e?åŒ`¥+âüù£8"1 Ÿ…À3×x‚¯Ù<÷·}ÎÃæEh2N!¿´Å…YU# ±w¼Ôè$SRÉ¡ùòxB:ÕeÄK\ðme«ö²ñlv‰b†¦"¨|’¥ÈI¯ºˆ[ØK9èþ`fƒŠT|êªü>ÈˇãÇC?Ñö6ämój’"Àƒªj“ÆI‹}SÔ/E_üðÒØ[¹ :?¦c%+—/‰ ÑLE„K…ÆDî/ˆÐ¸¸aâ€æ*4–bSÝW<Å 5•T½ñ¤yÉ#‘6”ÇŒì…ùxÛÞ¾¹—_4‘‡šÆîÌá‡fp4£,'±&ª…kSæÓ +/Ñá)_Ò„ êdcSú•8ø:K“0kä9”eÃ$¢$ЙPq(à}ŸÒe?* )c!þZ¸a‘0NÃ@ç÷÷g`¿~dÇØþï¯A ³® ªÆO]GkqóvqsV¼nÒÚƒâ*‹Ñ8Å ¬êMr¯Ž¸ó¿o¡f¤â#R“¯ô|¥+Ÿß~fc×¢é…Oƒ@YË?@Ù”óú¨háŸÖétNI¦¼ó%2«éBóõï?×äœ×ûÔñë1¯ìšÏéý±c»'ç`ãð…¶z½Ç÷ŽPýyÜ„ÒbXsŒšg1âïÛÀüõ&Pýz?ç3¾¿C©¶q7=e,Pû\?/=ÇjŒí!šó}ÞŒŠÞQ­ ãûœå¸ +üyÌŽ¿Þ'_çÌ>êÝ©ÿu5þEõ9h{æ±þóq²J#ÂUY˜­¯×?RÞÀâS_%«s¬iâeÍ£Îà÷?(åS œY¿ìh¼®ØµQ&] +0Rkû%ù¬9 J¤•ÂGð=A–‚õ¢P~TÆ䎄©5Àã? 95=¯–ŸfE5lT +/`Ma°#+Wr` &É#–49)ô£šÂdÉŸP3^/4›5“V’•šYôôXøÕt ©ª…á‚@ª(½s¥BU¨ƒWy–›"~oç[kñðèÖß!™x´¿šÈ5üySÖ~NðɪÌw#±ÅâbQ=ß½%_1å5–Þ*b·ç¯–Qög~íëÀÛX­3Á5+s¢ÛJ±«^MU÷æňªLÌÉ÷Ä ˜ÂÛš0|Z0:òT[{“ok[®ö°N|ûjÌ®ZYô#(ú5ò…WÚtQ Û&–JF/T™:U"t +/'e-Þ7V}·(1 +ÍY‡ÊHªM†Wþ¤<#ü¼¿ñC„˜TarRxþðÆ–;âHt:4ÕþnðÄõ\Ïž3Ø!ø¦å÷nOðÏ.~Ôj,æêûóqÚˆ{D‹·تrÖ±½>]Û MM_àòÄæF0–á ³ W +¬ê$œüE±3óªÅ8Äí™ÀÝÉ8ðo DfŸ°Ë +@ˆ'<Ïkt+­s/ÃîÆ_™9 o#(¹µ@Ìßœ,40C@ëqš=†sYsØIÁº(Œ´‰¶ï¦ÃQ;²ò`6#q±@-Ðè߬¤æ'Çfž—ë»;·pûùLÍT¸ÒvÝÕSg·oìkæ?·‰6W±µ…èa©¨¤/¿·ÿC2WƒVÑ¥h£GÓk»=Zø« s‹®ÜàóÁµþþ¶Ý!hùt *\„!S?ø^8#,1‡9n×L\J}u<¿ ·4Ö*œ0óÞ¨)ä7׋70wÇÛ°|ZÓ^"D=0ë¸5Àå3GÞêñëlg±‹=â Vç\6–RåUߘz}·™^}‚[š–“éÇV‹« +qµx}'ç ÆŽ‰@Y¿Ú`fÖªÂqP­äµÐŸ¿­=±è&7Å!À~³CÕ*@hùMJÛ–vãmÚ–‘Ó\:mžãj„\ÿ4„AÃC¦6ÑjfÄT;DƒX·Íî$ÇÎÙ y±Ð6°q…,e:3š=P"4^¢"RXÕŒ<çìQw¦™aæä³N¨*ì:PV; ÷ø;çê{ÿTÁ 7_U(ì‰÷Á°ر¯Í€d›(ÅiߢΉv–UDÀì3›mA8>Þ0bƒn:ClSàZȆ¿ +¢kžÕïqmC„Š£‰V?“K-_ +^XɧSôE}!ìaµp-Z…f +Šæ£ÇÅä| #T‰¯èÐâ“9Ÿ +®yð +¶pƒ¹¦6ŸÊçˆ`ÊÀg`g‘·Ì‰û$Þ‚R1=Îvcrˆ½«œ<ÇÄÒ h¼Øù8*§?g4#S⦓ WR¾×òZë_—ŸvøÙöÊ‚°£Í$á_‡À̾hÌ!‘ +Œ*™„oË!™ØÏ´¼ÐØ/ÜÞ¬¨fBK øK$d®ébkšŒÝŒËZ®×>_5d&r)¶7»K1‚;i±ø–V üa¯ïn-8»uœÍ 6Ѳ´Ö»ÍnmlW¯Ld„ƱfìnØMà¸Nnhºv³išÑðâËÞE8 +/{—km‰Î… #¸ÒÆÂðv¬6Q‰}9)ŽäÔÉ¡ó(Y6kq†$%*×þŠž7§ +îcòï²Åˆ±ŽfqÁÎñ¦æûâ×iZ;N–÷‰0ÛD¦;v…Ñ@ÄÄeT°Äiü}S2ËŒ–vz‘à߸¦ ++& ÿEÃ?óy;Zw„«EØEjö mÉé±TD]Šc +ç(CvX +ZrHF¦Š`éí:xcqËýçJ ˆ2‘à\œuÔ¤^NA± °œrz´í–õ¸Ú¸È…_wÚSšé¼ýÈ$/,Š.‰¿hÕöèœtªÕîN²0›ü;®uµ÷¯˜gÞHXÇZª¾kÀ<ê÷Õëù¥©Ú¥¿÷Ï=³)ešjç&lǾü¿Æ•TöÊóÛ`°B¹÷v¹G›Â ¢<óiYõ}Drö +ÁÔ4|‹þ˜Œ›åÈ„á,u99wUÖ¥æRÆÚ°NçôŒqóü„ƒÄ0&j¹FÂwŠ9˜Ñ˜õ~D<ñÇA`‚úë× +¸ìóîF—$<ùËb{ñ€«ò<î`Yíu‰xþÞ%<Þ.WÀæË$ÿýV +Ò组¥æ<ø§^üómù?×ßG"Ó<ø¥ð¾„ºPöRã%Ô-7O"¶¥ +OÅ[[6 ÈVËkòÉ :¿ã"I  uq£nS~ú +lxù¯Z×AˆèÝÅ¿ƒ‘ÌJ·ü.Õ.XuâRÿRA^ƒïæ׬M„.å¡y€¤íŒ@>…’nßÕ‘˜ +asmã¬qš=dF8Ë“öÊø_e_’$Ik·Sä~›“îœN õ—Žf­ZT/¤®¯Ä@FV¶ëE5‘î@<…Š³dÈ+Á‡0ø8Ä)r&S'S:krl—£”ù]Ï¿G æšh¶$#ÓéÇJÁŒ½¨Y†£]b]ùr—¶lU«(;LÍ®#;Š¶ÓÔ%ŽøE‰Õ¬wY€ébt:n" ±!Àžý¶fXÌR¦æ­„ŸÜ@cE3ÎVSj×ôæ`yù!ëðψS +F¢{Ùš+€hTR`òˆvËƘ¾˜ÙK7éO@–ÅòM 7ãb-¨ƒï–¼þ{§áªOn(½»°8‡¿ ¸Dz8òrÙ¯G§Ò¯WrŠ=]èžXíaˆ7ä{g ÄÅÔÃàËå"ö´[ºî&ÇÓ‰¥½§^8e…‘Q"üÞþ’+•ï[܇ûƒË7ÃRAwGOùxÎëç!2f\»Q7›àZÏdžZr¶Ò”Àð€hrˆùáNc¥è~¬!JSÏóïâ€!Ìö5µæ¿é$@WÈz4Uó\wI‰o/ç8âWOÜ­XbÙìU5îuBPœ+gO´Wºª­ Q1‚¶I5m ²qð|{±gZ*¿fbË¡Xû¶_|¯*YRÞ®Öraö]Û‹÷÷ˈôÿ~~(Ã|Èš‘»Í²ÿ¬‘ª Éh6™‡šâpä—% %‚„ia:䶀k¾÷ßWz$¢$›.ïÈç±g"pQœ +ê¼ðbHòÓtz@Ö+Ýã•Ñ~ÝbªíËzù`âXAŸSoYþØίÓ^cȨ.îÜÎ˦a–5¼xgZ‹šwæ_‡ŽÆ<ÎÔÑeÚË|ÓNS…É +!,ÖÑex_zŸ +s&¼bU+¯1&G…ö™Á…„¡®¹™‚Æí;Ï|*,²üà"µ1;Å1 ‰¼sÍÈŒ + ¡€T¾¿÷`gåïpžÉto%/¥$›°L{u—heRíÊŽ+Ì)O™3¿Ù3%="¨`[”~<3£ˆTþhà ‰uŸwžA +sTÁbOò•>s?*ZÆqµ±)!®ÃqôJ œüg³ýüÖ]šŽc±ÁFÏãZ!æu€‡š‡½˜É%•¶>ö2v&d¾­êOëÌòøð¾Ð%Öö<Õ&8š9ßÚ¯Q¼0Ê_üA(¸Y`¸7¾j¯?;ò7¡ëV7ŒÜ}uxá•Ú¤¦oD{øy—$àV’Ó¢þv;n¢Päzø¢ +pÒô¹ÜEX¸xYâ¸VáV~ˆ:Ò7·EgS'>$khÝà»É¯Áöl™NÀ¢t¸¥'r2¬ô®É–—Ô¸ÙE@pçIßÐìIØHÊàå™O0c* +¾i©°çtX}!òÁìÑyÈÿÐÈT›{2²'6¶Ä ›Pü„Lç–ØÈ? J'–ñ5{n¬šNIÀÚ­,X»£ªf›A=Q£‰Ó'èp„ÎÈUQ‡;¦ºÄ˜ …¦<ƒR³©ð2" ^U=”þuøJ´Þõ¥ÞР¢ìrÃJªrLlÌñîócNE彚³e·èøY™îãgeIo«®úÍ?0×L± à @KqbÆã0pùƒr"R|ÑDîí¶¡j^ï.ÅÍ?¾¥dþÇÁ^ðV&Sk*^|Áäu0B£·­¢žë¯?…ÊŽœWHÈŽÖF°ÒtÀqX1½d<ò¶r­ +áÊpfxkÆ;«¹£µóﺟð¯­}¼ŸáÖùýe‡';·¼u¤¡6!äPFk#ˆàÉhm죬­xŽìœN)î<šg°öñ@§ÆâÏóTÕœVÀ‹&~;':ᎉ?cµç­hŃí_mgUØŸË0Î\ îí£¤, +çÉm¯áúïš ÿÚk±ß¿|ÉÀï/s;;—Mg§%¡Á¹»þìÈ|l Ì/<ÈVöÎ[oÚ»V•-^óJÃtšRüùôæôë=y² +9¯Þ}›úWão AÇè¢t½e‡9©¯‰2¦ÜÌðey4¦ÖÿV<Ë¢œ»Y\MÅ^‡`ôßY/~>“KÃÄ{Â׎¨ÔÂrÿŠC*”›ÝG2 '}Ë“öp~îÛ·]ÒàwµÇþ™PyÈÞ[r¾0¼{»V#x¯ˆÞ‘L;‰®ú'7¢,Ï©*ßæÈ×9 >’@#Í›Wîš¼bßáἯc@"ÚìrSïËv¾ó/ö@´ ª!\» öæ!ävõ«O‡¾+öI¢yO—ìvD§/qù:ráÌs׌¦yà=9 àÎÛ‡[Ñãhbn˜/ +È…Ÿ £o[ƒRõÉ×?Ž¥U|9Ä2ìµ»Z ȵ”93ÿãæi"ØëÜ1VÚY‹S9é `×453cÙ 9Í¿¨Ñ'ø­ÚO¨ˆê8þÎJ'hˆO`¿³Íœ´cÙe¢ïXömÄ›f~ðÚ[c2+âx6 +› ³Ñß;ü!ß»°O8!¡)ZÓÍÆKÝÝT01^AÁ¹0ZCÞš1Ú#6F‚#!ÞXXC— ¦lŒ„ø„JùGVkYE¿ð.“ Œ„¾¥W…‘Ð%LŒ5?#ÁFB¼OUZÆ«#¡E¨ùEøš/Díì¦lÂ`tA¾^79AIh󬂿@@˜Ž@Úx{­9Ýô%”„ýA¢yß{r¬ ’Ð&!/€‘Є7lü6³Æ/ … 6D‚Û»F~k°^6B‚FHˆÅ¼Ê·5å"Ó^>C$4Þndºc—wy3DB›B{ #!ÚP"ðÁ©ÛC$¡ubáר©*ˆ6 +ðx ˆÆmgˆ„6]™R mpõŒ‰Ð-hD„ƒ;’0îš ­'CˆmÎOa"<`¯"Ì0KÃj +!!öjso&'ßopL~Eî !bmrç„,ÖŽˆqrÈ•|[éÏŸ$C6>Âæ ¶ƒßGâ'Wm²×“ð±!ÕèjA †GhÂPÅ­–GðŸ»?ŽZ½“ØÑ—¼›Á`¶;¡°š¿d;š +^5¡ †wÔ1ýKÙ(×½'ðel„5é˜p5¡eŒã$<Âñw¼ïám¯ñ¢­\â#ÄëQŒœø!7!F¹R­)ûa×ÊoD8Úø"!Z@àÉ´ÍÜ›¸‹ŽÍpox„F”Ë\ø‘è#"4‡èMÂ#;v +b¼q3Lâ#Äô-ãbDåÏÖ´“KÍÆT!lÍ&ÿr²I>@dx½’=°õÛ™ÊÒ°5¯)¼îÔ“­æH­“Ä +OÂ#ÄÆðkŒ<‘°ÃÔAQ$`¢Ô¯I&4P +ñbž¯ +ÐcžÃ˜YÙl47 ‘kø¶Æ?.û†Hˆ]Öû‘Ðz%i lk +Ä7DB³?È Í™]ÄHˆéÆ®fA¨æÛ?c$w<ÝJ2ß?¸—¤6…‘Ð…H„]I`“s´l¦øˆçj¼(6HÂÁjIh +y$H VÐÊœ’è«òŽÕ£qÎÇͱ2ß³¨ÀÙ°½E˜ÌÛ FôwÏ k¦Ó? Âë Ðö<Ž%J‚ +¡$ìÁ%@*ÂI€ÜPN¥ÔÂ#¹“]kÐH|%N‚&!l*â"D@$QÂ$´–üO˜„x?r„“ösr¶=G˜„ãÏ}¿þ0H­Ï—®Üö(Þ.„h.¦Ó-Z#Î}$HBt\év8)ˆ…’`…Ëç;^Ç­M„XÍz¶¬~Ë‘æâþús#nŒpÌ‘o-ªØÈrõ·?²«ÄËâ@¹Çå·®z°wîóæ(Hå®è`B$Ds°Ø=˜WŽŒŸDB„¨!gÍQãÌýÞRS Á†ÎýzYŠ +%¡#](B¾Ìñ 0BHÒkžM™WÆIé¨ô#¬çT‘yâ$„¥±Üè˜Éø„I­`”„4%’°ÿÜe¨kÞ%Å7æA䥡²|>ÏM“lªzøÑtZT®"v.׉2pKU­¶™+ˆ„ aÌñ†HH% ˆ^†H€a9úN©³söôzŠˆj%„¤g7‡k˜”¨dŒR†HЉOs™ëQiÅâð +ŽÅÿç8#¼© +Åg#¡ +‚n°jËü\B$€Ý–˜öÍPN£íÚðiI ­'høÅqýϱW^zþæéüÄ@ˆ½Xž³éZ¦f=/«þ‹'vä=1ZSynb$„0*ü¹ÌyùË ’К’©’Иˆ–¢a r‚$A0Uæf˜ÐIØ‚V –´Iȟ €W|¿Œƒ$DsroJvîTÂEƒ/$ÏIH›@ ¶à’°m$Ø&J‚u~ÚÆ“°q¨ì¥@IÀIèÙB³íd&ٚ̑ ~ÙKò&¡OËì‘Ð*ôS {±W¸•Û¨<¡VS(¦[}*påÑgñb{6{e¢ ád¯ŒËü§W \ Ý^E§²“"UéBû‹^Ò×jîÊÚpâÐ)Ã@þ«˜yTxÿd®È#IÖbì›}2‹‘gžòoævÊ,™“=S©Ô´^ada{”°Sfñ^`;e"u¡ÍtʨÆzsqeÖ$ßNÔñîå| g¥rnÒ-Ég2YˆšŸöÊâZÛ+c‚ÏÔZýí”qÕô<—«®¸ÚèŽÌ1W5§Û@Q— ȼä“ÁJsß-ADz%\8;¨‹ñøÎ$¿‡ˆùdòƒó¯£ëöÉ ·«Úç‚é]é‚Q.Øá´QŠ‘}2‹q~Û'‹WGúd”—B—Ìk·7s$þÖ=OŸL4i‘ËeigÈ'c^¡Of-Ëð™ Qq¹lŸ ÒŠ|2JGz>` ,ûd”u¬ä±ÐŸ?­}H–j «§³|ñ°3Ù=(¨Lö›*¡,v»]½;äô ì©šÉì‹(VF´«©¤†{ž—i¡*ë¨éЦ HF’ÂUèßiŽ9®)Ø^†…#nÏS-Mff—02¢MÇF©¶wœ¦ÁÊ:å vy7…hqPÈ{&àÈÊ݃€êåNb~y~Éû¯ÝnGâ (ÖÏ›°NšOµÞøäxí@Kïºà”ãqÐâ7U›ü¢TW.‘Ž®;y¹LAQ‚Ë»Ÿ+ræJ籇¥ÃY™¼=ÉIôØFoé€Vù¼(O–úž<ÚYÞô’€Òᇒa¥ð²=b(-þHÜÚ´.&[\Å1ÆÚ׿¸ÖK`‹žÒ$ ÂÛLFÒ¥³¸l·À„=“úÒ”?ð²oÓ"v¾ï¡K+ ÆPEºÔ„¹°~1püfRÒþ@P ¥_ò¡gõ7Sêä¿€G8?FÚÀš½1xEÑZcŸv(êɳ²í²™WgÊ Ò¥¦%Ê$ˆƒœ®€?еúÍÌ!9lwîŒÌ>ºtñÝÝ¡È€†ø‘œ¾ã^Ù?Ÿ¶?ÅçÃkKø¤ÏáÃÌÚà4ô4›::E˜UÆò,ˆ5~™¹rÍíø8¸ÂË•|ó8Xá;+m€‡?3«T(A²Ÿ ƒù¸BxÐ9Ïé2yÎK„xå˜@3ó¶ÐÅäí4¾ƒÿ*Y¶”A%x‡}l¼]|FwQnt‡<äÝ!42{:Ë0غjì Ñä43xC +Òïö\©ÒAa2C¹ue!t‡`ÏÝ÷‡üä U “3ódzàÌ>‚whÎw¸ÓJ<¦Zô„w°Ñ«¤¤½ÙïД—"x‡Ð*È"¼Ã–„‚w¹ÙÛÇÁ$ˆ¡|癳äÿÁFIàìÜéæ—€SwŽ´8º€Š¬Âwþ¿ àй[f>o˱ññ¹ï¾à! +Má;È9e|˯Ş.à;Øj5¾CÈÁ;ñöð“0˜“.|9Xï6KÆ(¼È8Î ¼C›‰ûAx›f‚w fr‡q¨v4ÞAŽhÃ;ÈOcµ[ð>î•JÔy[Ø×zÃ;8†!qìoÙí‹G(;|'¼BR èK¿ù‰ ´0}5 ¿þèÈÆvØža;xñ‰í §Á¶9'p˜sh>> àÅ,‚;4^ÒÞtÆBÁÜ᛬ºÄÔ¹ãÑ÷Ãà +a¼ 46›kH+lÛ7ÌÓ‡°ÂüO”‰.‹_°Ä!¥„í æç‡^¦ll‡`„²>6¶CØ!—!ÂZë—+Ð…!ì’ð%” »l! ‡ÚHC¥aód'¤U‘ö t?qÓU³'`·…ÍOxŽ¨líý$\‡t~×AP©Fv×AîCt4ÌP•A¸MqÂuàí +Ÿ”ÈÌëbžÄ>ÁvcLˆ_`‚… +ñ?:]’ŠÆuÐñB¸>ª×A&‡FÊã¸gB±O—9~§ô×J‰p"¸ç|.D·ÜÒhÂuÁ¸6¨„ë° +.á:ȳa\©<ã:´–åü•5'‹i;4^nØÁÖ¹€4ü€£[ ìráMnœÀ§£Ô3×í +ëЖaêëÐV¦* Ö¡­LDrL®ëЋÞÖ¡_¬µþ 7ë°??2\í­í¸ëÉÛóoÚ™)ŒðÚÉán¼ÇóÃB›î¿-8¥àR·oL{:•N°=¡J7È¿3?Ïd¾þÄtÈï/X±sÙÌDµûLowÞQ~½Ü»k“Þ¡š7NÝ¢Ãñ€†^îsfürOÜ]ÎI=Ò"^ÙñÑš/·6!³^n3¯¥›ÁI)6˜¼M9KÓNK…’{°þ»f¿Þ¾~¿ÖÁß_V£N$Ps'™ˆyŽ¯ãç„·ÑJd%?$OÛ½³ä™G8ò™”r<€¥ðÏ5;ûõž<~]I)êÝ·©ÿq5vRJh†~I)Á‹Hl€PÔʤÞ.õ@-¸wNŠ6µcñÕÜ9)ùwFàç#sRâõ­œ9)ñý‘)'¸/Ø­a ]%ðåO‹ƒhx|”‘²ÚÊHiÊÓVFŠg^}ßJ )¯ý2R#¥œ‘¢sÊÎHAPÜžRU?aóµ;¿Óö2lü‰JHweBJëÂ!e> +¬4 {*¥)iSù(ûX®|øçz&¤ÈEâ|”í#Q>Š|$JG±Æ¹3®ÿÉdŸ„猎Wžâ”ŽÒòªäNóe›(’(›ž¬WRhú8ÅžW¥£@–îl—Ç×RB;h´ôÊd”à±?€âëú¶ô Ä´’QŽØ…0$ŸùÊd”.@½½JNc0YþÉ(ñºRœŒ"úòª^øî\Ì“&ý;ËüÈE;E‡Œ²#› ËÝÞ<íœÉ(ûü³ TÕQØWÔß;ð÷«>,ëTÛR¨“O´#åCC¯EljjÜ ”© ¸_, ÄI!Ú¨ÙÚ.È:·ù“ò>ŸË m&l Ÿ± ä DøUJ¾±]9h3ø«Éò¯iÇã¡‹oEÌA¼£ª}±±îÑV´Pt´³£WÄgbl] ƒW¾RñKÏB`A>ñ0”‰€§G*57ʨÂ'öõîgjÊúíå +Ÿ¦ôŽâj¿÷²Â0Æ?Öök½MÁÚ æäÌæµ=/ž ÇâIIW{Å‘äËF JH½x0Ñj{ô?S½eçk˜?~Råúػƛ\£G/ +Æ>)vwÍc!ú®ºÚ/-ƒÄŸí‡(­Î•\pR~ý0E1m¬Ãéžÿƒ·‚ߎ±Ð"©ý–© +Þ$Ê‹k¡„ýfhgã%Ú`ã~å‰;ÃZ‚‚õUžÚ˜Þ’¿P¸ò%çû Æ +âÍ¥^µÅ€ÜÎ`„øáÆu¤ãQÈ€l§$åb¸uGçÿ.zãÃÉgZS¿µAãô:°j(WÀÊŠ±òØÂnUd…)´_´+…€D:ÏÝÞßyÓÞœì.â=s+ö+¿¡Í³;çÛµ=Þïh‡•D@¯ $™±à @»s<íæß™WÕ;½Òßuê LeCZ`¿´ÉHÉÎuÿ†ì'Ç)à?:ƒR˱Èuhïsëvõ}éÕÙ»¿)Ù6.¥ áçM¦ +Ïdëû»-óú–‚XÖ˜Kñó®uè4cöñ+U½½¦’áí +Xq’ÜÞ>³íÍ¢›hc¶ +#?CR…ßó˜‹øÈÒŸnòä +® ”-žœü»Ç³Ìv$^^¸DQÕã*ÜA{w6n1`]t"çã>Ó„ÏYãK$õŽ’—žÁ`—(“uÎú—ÁÚÛÖ˜}¤’Uth¬-ƒi‡ˆb{禜~…æa†NŸzéb XŸ¸O%Ó@O}eYsw¥À6±s: 6CTÔõÖVÌ-)œNñÒÎ-<¥>QJ +êçÑO¸Ð «LëÅ “@Õ¦4cí,¨?Ñÿ é’ô,qº,©ÈE +hAز +eNazÖ ¢ Dõz›|ã`%<*T³mL¾ÅV€)VÙAÅ¿DDÒhÉULÒÞmeÛ”˜ÿñHQGàáÃnõ©ý¥f$ä}e7Je{`ðJÝíOÍDÝ~ä¢~`/eãv{=±h·‡§Ç€W®awùÜXfòr3bÍ(yòTE¾¤À`TsqS”a[£ùÜÜ倌 ùt›SÁÓ(œ­ÆoF•K¬–yuƒá€øhüûÕ¦€ +ìa_¾ÌÔ>Ï‘Ah²pTM½Ú Ô“€ÙºÎ![â®í»ô”úC"± G• +Ýÿ –tÁÝYR¡¯¥³†êaô}püdƒGòàb‘ÿ¢>2û%s-¦XÈ Ÿ+Ý$_Ç—еû€…-Ô«ã#BšØ§y´Ý+sdœJ&- x™ÉÚ³z$íxbØ`®íº:ç|ÛÔÔOó¯Iï@#_P_)b‰¥Ñ~5òÖõpåG'óª%oà0ùy +DçYç£A¾xòìàŸØo…Ô½yù¼a8ß~œª¹ù•‹ÈóÇÄ/UÆë+”­Q;×cô®n±ØÉ*»Uòûþ枇撟ž¥ÿ`^6ÛülÏò1‚íôIJ³¥>e‰vÕXx¤üÙ*˜í? Æ%ËŸ¼8.æaó¦Ä÷‹í: ³hY¶$qeç-ö ¼ê%Šœñ§žK‰æÏz{¢{…ìW.:²d¡2¹l±rù‘•èy…>®–µ–öCSYÉr¹Å7õ7§iOÙŽŽq`ï¥ô]”s)|'3–Î.»YÙ`òù°8^LZ÷é`(kÜlÆÛmf'kä‚Ÿ^›òëûû¥œÍÜíÿ|lŠ$l$aŽCÂ"iúMÂFŠî8%,r£Ÿ-?cðMÀ.æ¨ìšÜ%‚^ˆ”÷æ™\a¸Ò+ÁôêžîN¦­wú‘äþä/&õv$Íη]WÃô<Ôö’'Jj;ÀÇßüEѨ3‹¿zûW9Cöº×§™"½²dÀR¯Ä8î7½”uˆ%äôÊc4ZɤÞC±ÄôÜk+–µZ8¯2Íþ9K$æKè8'Ü's +¥çûüEHmÕy8‰]WR‹n ·¶Ù#GV9) Œ Èènéb"Ü„•? ƒÃÀ›‹M£¾µ°”t”:X„qãÂDY:Úºø?°;å?„«$?û²©ðÚû8‰˜^‰f`{E+ N¾¯ùÏ|ð7€ÆhÕ¥ö‹,ʾÏXø“q¤+ÅâXç•ÇZé„""ÒÅÅ”Ø`žìsäª^2å"ò=.ü×¾ò@ætÙ²ŒyZp}æ‘v¾dê1IG½Ò•R1VzÌ’Ç÷YG»dÂí½R}8³$o<ðBÉÿ(÷øFàbž8J’ɪæÓ¹o¾ `®OM½† +Ðaž·hAZ¹TzHÌÍ=·æÃìÕSõ1úx tg^q¢}K•*N.V¬m}kð¿¼òºß蚦ä•ÙéýÊ îÖ[­(iznÛ8û¦¤%7]TeÞô×aã¸ñvëÔ]EW¶—?;—7‚/̼u¤,?›þ1t̾.à$Òy „ìëeù£¬ÕB§t>'LÙ±XXË'§«Ø>å…Yƒü÷Ì~´7‡n +3[Ìá ¼qµó¤§|øÂ#Ú­ÙöճFá]x€#›î pU1º/<)ß÷&¢ôcÁï7rÆ߯ë#þû¯ÿåã‹E>þ×ë|þ#þ©¨­ýÄòóŸ×ý\¬ËÉ*\_lÕb ‘ðûã¿}ü§Þ÷?ÿ;z€Ó&J0w¿R Šò;)æCSÂ}ü*)/÷;æ<¶å}«„„)Ku à„‹r6plÅÎ'’WƒT+S~´' v½Úá[8žÀe<ßÀU‚8¾Ré–s7*] +ÙËÝŽq|šòiˆø†¿o„Ë~ÝøÑ3Žn‚Gï‘€à]2ž¤h2Þ^Áé:?ãÝ=áìçû’|þ¸L¿˜¬§I»¦Ò6ɤü>(¼¢Œƒ@hÇP6!G™€‰#,D3›^¶Ÿ2$ã ˜Íù߸€Têm²Í¤LFò‰î^Zïæ£ÛQ`>ûFðj>m¢ï¹=OùyP" _h¡„ ðP4‡aFûš†óŒðŠØ†>ŒµŠvS"zÈ#µÅË{>—vXPÈî|ØV6)E|ã’ÖˆG•¬we°\mÖÓ¯(Óà„\èYQœr[!÷ÿ¹8_¡T”îŽíÁÄÔ0c¢Ýô +ˆÇÐçA!Ê&™%RA!Ž!‘‚à¬é°lÐ~œÝ_B Ó'×ᇨ +0^Ià:„͸!6¾Z™ñ§E¬äHÁ‡k9nÁHÆŒ? ¤üÑ#òóB`ÉpªÆà™øÞ¨ÿ‚`Õ ‚ò¨ÊDx 0êåd3œíüAl ðlsa‡ûæ¢ôšã¢Ý]?¡tPX_ÌÄ}¬rc‹ ?3ÇßÈìÄ!æP¤ÄpãÐòûB ’Eã°a”zW®K\l)´UPŒy¸¸’ÎG}´¡'•±ê…[Ì€+ÔÂ/víÀ0Ãøƒ¡>püð³æE.§yE{vÁp>Ÿˆ{?¾í§—7†\РLäå.\ƒ [XØ¥m΢ãüÉÔÀŸBT¸u£Ù.¢æ‰Y¦;23.qׂ‘­ÊYCÈaNögR´dLºx‚ÙJ\¼¶}§´ÖÕæ‡îv³­®iü….Â0ë!N&§÷Âx^›üÅc!¶»FæAê•O}'„vÅÂTMÒCáJóQîW|·E2,LÏE´½…F»,;°§îœp¤‰"{ ÁS„K¸LfŒÐ%òC² \ȼÐËn Í9øQw +œ ¥#eQf5››¾¾ø"á!;Í>öDlýgRö:ÿúa†ÎßTðÈ,ÑÖ*„»I£úV&0×nX|¾3oô”šH¢+÷–Ô‘IûPµ:6n–¢ëáû¡Mi(Å8\ÂWð‡·þ.a\püe¯†@ËU-¿Z×Em&AY¦¢zš ¼ k‚þ pn +/ßPˆ‹8‰6{žÇ½:a2ë·´mµÄÔ\õ´k”ä{>"oˆt¤éòGÔ½}X>ƒÉ¬¸^6°ÊÞºE¯ÚÔ/Ê|¥jÿÍw@Ø,°h¤þªWW¾ÕhÎ;y€¼…p§³¦ž)¨²µ ÖüYÇáÒSœ»Þ5S·fà:6š3Uº`1“}7-e6å‰2|‡uwÎ>™y!ïW9Úu. +2õù w3µø{¹ÉW%ûWÈrúú•…ö¤+ŒåÑŽÉGP#æ›oèÔb±ƒ;F`±Q8i9Td|½m²Grj +¤1jªp‡¥°›•›ÐZg.=í=ê^³Äñ§¾i³«¨O|Ì5Dz8ű$_Œ«K6¶%Ö}(9Ç8„ËV, iÛÄX4¼wç!´%›ÐHòw­z%C›¯·¯0¦¤’dJ‘H&Xû)-¢"…XݶíÒæ_ÛÄ,5çS¦IavGš&¨po[DÌvÚ&åÞvì$£DH^Q«e1?zÑVî´Žoa³ZÀ£¢Ê7àdôwµªâMÅAþ¬zÁTÔC—lA¾.q¤TI½R1H•Dvé:UIdŸŽC•,ï 3ùÊCM²rgI“¬Ü|V%+·'UÉÒöµ*™šô­JÂ=õ¦Jl#P•Ì"V%3Å UÉL9$U² V%›BU2y%“ªD¡‡*YÛr§*Ñ•y*ŠÅ+Á­J”Ë›OÄŒS•TÖ§.©¬ˆ˜º¤ÒÒK]É»’ÚÒ%ÕÖaê¥[—D†ðs§.©JÙºLpê’z¥B£ ^VxÖ%믶ÞþþXÙ\‚ã¼Ú»*YÚœV% NäT%»i ±)R%Kتd‰™·*Ybw«’%›ee7@xª’%¸UIðû³U Ðlë©Jê%qhUKXëV%©€‡*©,ŽT%eÖ·*‰%½ß4I¥»<5IEšQj’Ý”&Ùj’ZR0ÞJR_cšDuR“D—ž­H¢Çu¼)’ÊܳT$`ܶIŠ–ñÕÇ¥T$tpo5bú§!$ÎùÖ#€E×#¬Ÿ´õÈD€ÈÖ# F:õÈLzd²#R#“sa-²r.­Eâe·‰[üçÔ"K>‚q°Ùl‡Q‘€Ô"ÃoZ$¶©Î«Ð"[´?Z¢Cô;¯ÿM“¶Éù BÕåÍ>&…ÞNRàÃÿæWo¿ÒþÙÍ+Ž);òûíW ìAEчqP‹CL +»³Å” +‹˜ØOPP*“¨Á[*Ë“{ç1Öîcí'ž•ìÿ!îÍìàâOÄuëçˆcE5Y#ÚQ•7ˆçÃüÞCÆÅ;µ±è7E·6ºué$Œ{jãÃج·äBn™¨±Î'jZo„Ò»º5].£†UÑ¥èl‹R(éÏ®¼>üÙ­Ú¶?;ŠG”²ýÙÀ´§?»*ñÏþìÚ·J‚0vo­¬’O ÷2˜qö‡?»Ü,¤?»NJá•h¥—ôÀpz¾¥øàë%ÍäÌ®#%“ùväúÃðµv•ÜÂ]¶€”3;fB>iz©œÙ1Ù×éÌ®,½˜Î쀧3»ny:³‰¹|:³£+ÏáÌ&bôvf*÷Í™ +ÌéÓ™«.M¿°ûÛj±¥A/_vŒîc{²k“1*Ov|tܹ‹ùŠUfnt°‰\0•:¯Gvõ­Å~„.é‡à‰Ù»uhŠíÈF*4*(°v[ïAY|Á:ÙÑnïŽì ñÐlÖ©ýðc,2§Î[˜%S¶;p‘A›¢oá}”ÛÖ,ÔÐ’îÛ±’~ì?øB•Wp ø×cÌ]ÅÆÓZ*) *ù®²¹2^–ò\tgé_2“ë=\‡i~oݤÝ™êÚÐ|âñn¦C´íРヿð;PdënŒ‹™TضóI «)Ëh“M>ØEãän‡·äÑ/|E 6’å53«çud%e°¬[|¤“ûàç¾[z©¯=Ž¶dFÕRŽéÍÕ¡e¢˜ÆxÞžˆ x •Ó˜Rm{Eô£‚v›˜!_Û½-"þâeìíUŸ¾›¸UníÑ\o¡Ø5*X[3Ò¢å;g±(‚í“ÛLá”UùãU^W%ðî +ð[c_Q!í‹@]0¨ëokê¦çÚ—Îz%ú3ø[)²ÿ<åÛ>¼o¤¦“K´gïI×òÀ‚°tiB˜U·7çlÊÄGžÔg8 ÝÔÊ^=éœÃÁâŽêÏ(õ ðW¢¼„'þd6¾ÊbhNˆýÀªšÄJƒØ 0+c«Ýü[íªU¡¯ +¶CŽÁEjó'‘;|>æ9_ðb߯·o¬£,Ûºû·Û½Ž”WpKùó(t¾;¯>÷×)!vç†bÐýW>îG¢ÊÀù +ÏÑþ†&ñADÐ1Éìáû2|þ¸4»xi +'ŠþØôZ“òû tD‹ÒîMy1t=”`$F¥Çù ••ëf¨Ë`YÐ AYhΊeï‚ðtÜfänÃ4ì'Ü +¤EDí¥n… …1žÐëRx1Æ“¼jpíh#ŸåFQ&¼!üMAÀb¬[#­c¿`ñÎÈ£ÿ0´P4‘V¶˜ˆ¦öKcçïGüN"féê4¶V3²ñv‹8þ¨·Âb¨¿wc ÞFQË€pDâ{t¥?&`QBרcÆ¢MÍVB”#H3„ž^|²ò“9ˆÛùyPnlFf(@Ïå!¯@#Fûâè3:ôÕG3†ì´U´&•hÆÔ^ÙvPw=ž¨´NñIF…Ã3‹¸¼hœâ›þ(pÓMB›»ý ÖÌçiŽÑ.dž%À6*ÜÙ,™‚ôpºªé¢~X´õQ:“ù¸_ÜÞWƒ“aQf?†FÀÆσá!¥c2Ê}®2Ôþ‘3B0;øPIyPýû Üp¢Í)Cm“ˆÁ°™ ô¾5É[XmÌnŒ6å1A°0U?ðÊ•Ò˜Nñè„W’ù9Οt0üFËHÁåðÅ;¶£°ï~¤ó®~ÖLÐ@pÇæÈ. ,»ãŽ#k€ÕµÉoÌ24r0KGø1áÕÍ,7替06þ§Ÿ˜Ìà®!yÈÉþ<(X1&±Œü¡·“r#÷ér3Q¤{Âê®9O—¿ƒiVÁ*,D{6$ð<’¡U­gÏÌ:‘Ž&Jmïüd6ÍÏÍÆ*&¸jÓ{«t€á+…© 8E„E*CÓæ«V›KgoR^N#@{-ºŸê´8ˆƒ2˜ +´Aø?ŸSÐOž…×1ë!Pc:•áÙ²èrÖ†™ U A¸¹&LEpûAbØçAñBÿúaÿ +À¥®io +³Ý?\š“›çÖ8KêÛQ0┇äfmP&;»Ó ™•‚¢:1"ÿauÅšG»"÷W e H /ä¶åV¬|ÔêŽös³MI²åñbÎ}˜«21’šèöѶ¨TÜWˆlL,ÐX¡½(l³íõO¼„í>ò01&cæÓìé‰çQ¶Xà .”ØVËR'm× h~ÜIÁ +Ù9WcýaCsÑÿz)Ó<.ÊÆþ³Â:¥àµB_kö߆QÃÈŠaÏ4Ò.6pi“Ðd$?Ïz»±ÁR"Xóžj‡SÁ-,=…oØRú"¨&*ã¿ô‚f¥ªë÷óÖ¶ Ù$^-K³óRŒˆF~7\ßBA%°„²"Ùpé–ÌL.RjE;—M.•Epž‡Ë$ØÒSÙòi›œ)ê‚ +üÉjäË0¹¢|…ãÔöÒšRVÝŠ;f-nè‡ÙßÚ”—'åV¸w0½tïfÊcþÀÅ\/–s‡"`_}ž*Äø…xÔÜŒ‹RÝh~\« +Ìž~AŒ´80Ï×!ÅW<-Å—L¤ýÄÝV¾Â˜x©}¸¥6Î3%tkÜÕ]B>*ÍŸr‚ÉLËY>Žui2lb®4Àm—,†˜¤a²PWVÇëàµzì±´c/¦Ëð´Yy#MQ̦ðÐqò†ð=å{]oÍ–Aýß•=I+¿?A~îó~T|f¬o‘:%_ë²!W{èè»Wûë|ú:;¸¹^wp©\¿Ž'˜9«ƒ㯫lo¥.ÕãNµdÉQ™#àWzÒ£’²–ÖÈuϤé§i´u¤i#a÷ʼù<ÚŸ'ÁC7E0"—íÚö—@džÅãHï/ÉuÆŽ·CK ‰Û­Gèo?€÷jdE¾…—£ÿõ$ŒÐâ+QÎ8bT۸׌—Þ¡’Æ¥¿+’!%Ûè¡‹¯4y¦Öø»"y(—"¨q—ŠÄ†¡ Ò4FÛç4Þ¬ç3}C6§ÏFÊ£È|ÙÏSAà,æK(ÀD.f©b™©wû_²ŒîüEý­®‘ÜZÓž¦·g¸–uN_Ê¡¤÷ˆÃ<Ùqh´û ®#Ĉ}çy¨_ë•ê~M©Š³Ï ü=·óßj:–Ï…væ¦ÐYEŠ]`áÛùý:Hù³r¥ÙóBF9_$Êù¢N«ßo”:+OM(!^.Î7jA~â s…Ç s9¹åJÝ¡é/¬p>aå°â@ZcôNð'c;000õðº3|É¢–b-…E ¤g£l±„¦ lŠž÷¢?ÙQ+NÜoÖÍIYY®Tƒ–¦ p5[š®t*l±Ö¡ÒÌVžäˆ’·Òê@ +ò£m+c¥7bŽþRH—]Ç•þõ« ë*ý 1ÂûL©å*é…ˆÕlÛÙyR`¶9VS~à»Ðg» ÊÊýe'4ã6å(] Nç¼Ø €R¨¦ýuòB3¬¸¼„çèìó‡»Ý’ +}ÇñÇ+ Áohͳº°Ênbû¾Ž‘ؽÇJŠ'ãxÅÛTGf÷aO4ûø¾Ÿ?.³µúïH±Ê{’?Fž9ˆÍ5xý&¥úÕ´ïL’2°Ê©TˤŒ—Ò,S¦yjUx§3€A‹*æÞI"Ç(’J?©Šµ¢''âIå“Ts¬[r2 áʃ +.­"nªq RÅÂWIA6zåLáÒó׎Ùú<(÷Ž£\ªÏŽ… ¿ÅÇeŸ‹‚ݬš(b<[)bÊL¡¤xD´½Vfšxϱ"‡½öÕî•Ý€½V†Œ±-¡‡â…l¯ÅªWªvØkelE.5 ÐÛk'Ñ^ûÆZŸ¸ò8lv3awã/‰;7´_Óˆ±É°„…QXfTÚß(ÝѶ“ó…Ó;òt_¹q¦¬ºÊ@×!‡ÃÍOPŽ7¹sЫ¡«mÓåÚ”°4p"éigŽíQ@äeÄë¸íȘÜåëy,'àË¿¯÷Ï<À€E +Y_š1ºo~ìK³Màs†Öv^I)r¯ïýI¹àjeP?nʯ:ô7¢\|9îbF?™‚ºM#±Wº¬˜fÇ_Ø‹Â%iŠ+¢2æt¬IJ]Â6 Oº¤"Ø.yõð/b¿ò¤ô¡«"ÃP#ÍRÉ!V<„Äæv# +ŠÊÞúmʇM2ÅëÕ}P Óø‰ò:l‰zë0ó%¶0?Æ+0™D5r•sÿ½@ðõynÍãv(ϦÞG‘]—NõØÔ]%§ªGÄøž“WfFQ×]?@3L¼vψ3Óc€ã̦ «C[KÅyˆº³ê úìim,e)”­ê2¼ÜC¼(ïF®nK»ìÑ8dŒ4àÝG›?(ËFy¤n‡Â 1ö°û´ÚïÞGŽÇ°p—°ê'eÜú W¨§ôÒX`®/Ü­/RfîE³ØF¬,³Qp5O^§{ŒíÏ’H +—ѯ@ä‰b½÷9çôÎÃÎD¸[g·pg¬ §‹·¼Øù×cÛ’ ¹‰éõÞ‹Ôå”CÐâëM¸8Tï?¼{IòLwöØÄâ#eQô¿„böÍØ"à”îÀhšßc£‡¹?±ëÎO(kƒox}ëü +Q³²K¶ª;¹Û^…å ú&>¿¡‰]V^¶÷i`/vƒwí»›ÀÃ|#!@æç1VCfNíz¿bf¼ƒ?Óù:ºáév7ßä„àòÖ¹H²9¼H_²µõã³wòq®’ ›ìù-HΙÇxÞ¤ìáû OÞp®ÒþŠçØý𲸛»=Ëó¾LU1Ø~Ee>GÝnÝg¼­Re¥©ì¦î÷HÒ(αÊ*ÎUò+fÆ7<ãm‘܉=Ýìäû‚ü¼H( +&Øݤ´Tú÷µ…³ÀÒìÇù4Ze¸ã.iè@`Ýö§À`Ú~Â_Ò蕈i<, +Ù/Ñ‹çNP4ì´Rvóù”Þ§ÔÑ8±5Ó‡Ë]÷•õ¼:m/Z$èi{шžæKoçù BÆXSפ޿S:úbâ¹'úÕ±BIy`†E +—G‰â¦«!…U^Ä +ÀAä*‰¤Æs^K(µÃI#,5$‚G£é 'ÚËxj‡'MxjéI¢Z3ŽÔ1ƒ˜j¯ã‘ªã£ÎmBUñãDUKSP¨iº~„Í ›åï8XY±†V£¤¾ó#F¯†Wƒ^*{“ŽYÕ?! +€÷–ïÂqße½Þ¶M¥!5‰\dBÊ>É™JF k¢¼> ³;‰›f,Sâ¶[²SgßûÒî¶_ÿnÛ`k>Pl´µãБ”<˜Üí½¥ñó¨“ÿIÉR~Ö”_?tåÿ/ܵfHíxWSþx€|‰ð0DêÔAF”ß›À.¢ðGIˆÊÆEˆºðøðIAHB´ÃÒ˜„( +=U«'ÚŒ{À|,ðÐ^Õ2n†²MYÈj‚‚+ƒã \â/vÃÛßè̸Ê^tòAör·é•…½B8Æñ +à>á¾:»I(g7/Ôl8FB±|Ž•5ªÔùŠ=[þHciàÝ=áìçû’|þ¸L¬Y­î‡¯†qÊdR~”"·5uÃìM +¢jÆŠ¼…-=jÖG6c)ØwñÖiTh…h3`d(4ªU—žm†TBù‰¥lÞø‚tŸÝr„é… ïÀ¾¸ÙÉ©[†v´‰,ô¹)BÃW#>Ú—âK1ÎÀ½¹è _k ¿t‹Ø9“U¶áOd½ŠÙˆ¡å#3_ZéSÀ”ný7’@ºQšQsȃðëáMIÛ>V[FÞ(`ÿØ“ûN +b¶ãˆíÊtðB‘<Þ1Ú&r3Ên·7k\:'%àrêmf‰ch`4¡lòdö>•£Ç•oL©öitŒ„ˆ§ VÁšL–žKªFrSoO@ö]tæV€ex.ç8òÐ{qº(hífp†Â-6%¬«h#ƒ}Dqq²Êý`²® Ç•%ìæàäÆ gÍeŒ}µïánqW”¸¬VÁõÉŸtl$,ÚŒ³3¿Å*6ò[›õXe›ö"üÌ,#÷ÙV¦ô…{ƒßo”ðϳ͉e d¸‡*÷‹c“ÛÛUeÞaâ±üÅÐB\úëÅ[ ’‹cK1ÀýÒv¡ÙªÒèÆä,VÎõ.ÝÑëǶ½E<‚B x??ÑæÊ(3]ù‹y†'â®2:Š–÷¹_ºìbhµ˜a+wU€ªÔà˜˜šîxFsá­ŸÀkÑQYñŽ¥ç|n +Wíåðä–¤“«ùR¬÷^cFy3{ ŸAÎ!b‹žspS!ÜŠp–] P‹1÷ìûaŽ™ohX ki–®"ÙÆy¾ ”úÌ]s3€#¾ô°d5’ú_Æö]v›¿¸ôÙ/ +y ^àxÅ¥u§9¼Ž>cWéçŠöbð±èhFÊW1HT!"ÝÃ/Œœ·Xº2·ƒ,µâ³è%ùùÿSò¨+üë‡íËšÜEÛõ¦z?(€Ž]º €¾C¬(77ÿn;^ø*:Y5C`…òöDàaQS)”¿H3uíËîPÈôÔvO¯om ™M)*º¿4.ÂX *ä È·(™ ^|Â=ÄuB´™¤%›äÆ>n±|c»pÁ '‚õ\‚95•¥l‚ÆQÈQ0´ô×d°<ƒœuÉn˜ç6s$'åÓʈì!ÝÒõ +·-$7…2{HŠ7ÙF’˜¿°¥…3L +qVø DaRà¹[7m½G‘踮  ¸Žù¦ 2!r)\lJ&´%-¶•ÀŸ‚½óÂ(_¡À³ãq~•TÆ9cõ”ÉáR@ŒÄú3NIщ>ó[ï(§AÒs"h^v[߶Gˆl¸í‘Á»±46†5ÿ<¶WoOÀíÑRÓò¦¶U ¦8E{ÑòœÂŸ OßB( Å @h\™ýòz3´(O‰AÁµuž7¥dÛ-9Tv›Œ*Q®¹/ŸQî؆ËíjÆ‹Ö)Qu—÷)Ñ…™·DE€éUŽ'Ú‡‹¦ðKïSb–¦Ö1‘õ°gwÛÇĤð˜¨É}LD´m¹õeœëÈc"ªNèü‡QŽÝþd¡çK›ƒlsTðM‹ï܆Û;uh/TÝ'Ä\Ããˆñ™§€¬c- +Vdæ ½*ý8"¢_HüÑ·5½Œ +5_øˆ˜”<"â7bvñÖyç‘•±Ëa¢c’,D×x´ <¢0ö|Î3"*_ßýx„FÞÅ`œç9‰ˆˆ–3‡DðÒ¨>$f3-““üe¿‰h_ë<$â#Ò´Y}»¦¾Ñ|êA×uŸQµºå1Ç•GD𚎇˜…ç}…xý c„è¹wi_"€ùž‡º)ÎeËÛðSÝ&ퟭo–M)¸„?_$Êñ¢8ÕпzP\ñšg2•I¸dLë]Žäf™v°ÚwߎÍy·‹l#yøx‚zZ’ÝšÚͼóe!å•ì˲ÚÓ'IÆŠê,å\³µÄs9à·èsߪÍ}ëÐ3ÜìcsÝ=µþD$ëƒK}b°oç"DLÏó‰­†Pi1b®«O;—6Aí›:ãŽöJöÉÐ#b;~lÖòjÿúÝë™ +ñÃÀXÄòGwcG›¤Ëq+'xËÉ‚GÀʃaÐî5Û9#£¼Úñwz%‡mó‚AÍ/ »*”D<²Ï¯ª^Z?9*‚³µ,«ÞŒöž:ˆáò0ÚÒ6wòÇ‘Ò±lkµäѦ˜Ñ5²‘Æ;'ç<2:Ñ-këºr Rœ¬¥|ÉM-ÍÊ©Úš·ÞçŸVÞ7£r/Cª£7Qˆš@¸±…;å6ªb|S™;f¼ê#y)˜'€y©(±Ý˜’L+£¨XïZ ©Bls¶F–’å#ï0eáb$_w)q3%×`K—íNtÀ=,üNèd´;%SËýFÕÂÕ ç5$Ùsï¸wwEÏcÜò7quj +DÒ[ÈT¦¥b$¦Ò,*|`Q ¨ÝoÖ, ¿Ï'*¡µö+´€ÒÔ +·¥º¸vns&¶ ñz¬pÄJi‡Ä®›‡–*Wm¸›,WºªŸÌ aX˜:è"ú±È¥¼­Ñ¸ÌIv^KÇ$¿QP¸Ó­±X-ù»õèT‘-êNƒúyœG¸÷¿[ߎÉÕ^>½c/?÷÷Ž½ÜæŽ÷Ž-!¡äxïÜXŒ÷έ:SÚz3+Þ;ŸPÌs´¯~Ä{ÇÖ½\<Ø/á”+Þ;¶*ˎ✣Ïb¶ ÷‚âq¸wÌ÷d,6ý%%Cî­|Ær‡Á…+ãƒàš+­8DV+Ü[^ÙŒ÷ŽÞ㸤俶}>ŽèøCK0ðçx ZmžáÞ‡–ùŠGû÷N-1R‹x+:Ü[vs†{w—ÌoNÞ#Ü;´Do›oìGøÎZo¡¾oìf +fLñÞáz}Ç{ÇW¦Ã̧.XæxïNÇŒ÷ŽžOWxvÏ.•µy#wÆ{çù7ã½uBÎxïUóû,㽃äŠÙÒõ>â¿7ïjIkMxƒŽxoðcIî!3~íÑuD|g7"¾¡Šçù)3ÇùN7Y†|Ë“–!ß–5{?Û—AßP“ýXÔ]ü}Ùf…#ê{ûœ½}å.Ïõ„Œúî]WKù£)ŠþxR~ýÐ!–2õÉN}l¹ƒÝÇ–òl0¯6 ¤P)Ó"Ÿzh7E»6ÙDŒ^réöâ7Ó_};,[n½ucäEÄŸ†öhtòëÏ1¤tlœá‰a=]-Ã!Ÿx»-Á3<Þæ¾CöBê+îtéÈ'Oˆãþò Sø†#<ñøŠb ³¾Œt7÷å¤öT=ï06éWº0?RœF‘ý_"ûY”‚“C)6ªs°ÒöŽOÌWx¾öG ˜ÝØK0Ýy.É[€¢.…Že²$«©L\«ÖWdk¼­’¥¥;ïSÛCSûX¥|‡ɥ¬q1Vöúv£’Ñ£aÆŒ±£Gém:¢G·»ÉÑ£ò69xÔ6Ë1{#ã:<šÔÔd°<6ÌrØ%}6‹Ùv +å}ê=z0¦<ŠP…«rlB@—üÆã£üŽ¨ý—¡£Á‰Í? RTƒmeÁ£Ç~aðèØ‘¹SaiÏf«ö> + >> +>> +endobj +5 0 obj +<< /Type /Page + /Parent 1 0 R + /MediaBox [ 0 0 368.503937 283.864563 ] + /Contents 3 0 R + /Group << + /Type /Group + /S /Transparency + /I true + /CS /DeviceRGB + >> + /Resources 2 0 R +>> +endobj +1 0 obj +<< /Type /Pages + /Kids [ 5 0 R ] + /Count 1 +>> +endobj +6 0 obj +<< /Creator (cairo 1.14.1 (http://cairographics.org)) + /Producer (cairo 1.14.1 (http://cairographics.org)) +>> +endobj +7 0 obj +<< /Type /Catalog + /Pages 1 0 R +>> +endobj +xref +0 8 +0000000000 65535 f +0000102423 00000 n +0000102123 00000 n +0000000015 00000 n +0000102098 00000 n +0000102195 00000 n +0000102488 00000 n +0000102615 00000 n +trailer +<< /Size 8 + /Root 7 0 R + /Info 6 0 R +>> +startxref +102667 +%%EOF Index: lxp32/tags/1.0/doc/src/trm/images/instructionformat.pdf =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: lxp32/tags/1.0/doc/src/trm/images/instructionformat.pdf =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/instructionformat.pdf (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/instructionformat.pdf (revision 5)
lxp32/tags/1.0/doc/src/trm/images/instructionformat.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/dbustiming.svg =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/dbustiming.svg (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/dbustiming.svg (revision 5) @@ -0,0 +1,1003 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + clk_i + dbus_cyc_o + dbus_stb_o + dbus_we_o + dbus_sel_o + dbus_ack_i + dbus_adr_o + dbus_dat_o + dbus_dat_i + + + + + + + + + + + + + + + + + + + + + + + SEL + + ADDR + + WDATA + + + + + + + + + + + + + + + + + + SEL + + ADDR + + RDATA + + + + + + + + + + + SINGLE WRITE + SINGLE READ + + Index: lxp32/tags/1.0/doc/src/trm/images/ibustiming.svg =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/ibustiming.svg (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/ibustiming.svg (revision 5) @@ -0,0 +1,712 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + clk_i + + + + + + + + + + + + + + + + + + + + + + ibus_cyc_o + ibus_stb_o + ibus_cti_o + ibus_bte_o + ibus_ack_i + ibus_adr_o + ibus_dat_i + + + + + + 010 + + 111 + + 00 + + ADDR0 + + ADDR1 + + ADDRn-2 + + ADDRn-1 + + DATA1 + + DATAn-2 + + DATAn-1 + + DATA0 + + + + 010 + + 00 + + + + + Index: lxp32/tags/1.0/doc/src/trm/images/resetsync.svg =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/resetsync.svg (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/resetsync.svg (revision 5) @@ -0,0 +1,289 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + CLK + + + + D + Q + + + D + Q + RST + PRE + PRE + + + + + + + Asynchronous reset (active high) + C + C + + Index: lxp32/tags/1.0/doc/src/trm/images/dbustiming.pdf =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/dbustiming.pdf (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/dbustiming.pdf (revision 5) @@ -0,0 +1,1192 @@ +%PDF-1.5 +%µí®û +3 0 obj +<< /Length 4 0 R + /Filter /FlateDecode +>> +stream +xœ¬½Ý®æ¸’x¯§Ø/ÐÙ¥'0`ÀÀؾ4|alÏé‚‘eŒ» +øõGë'‚Ü™û´kF¡;(}EƒÁX‹Áÿqœøï?ü›ü/çÇ?ýËqþ¸?þ×[úoßÿÿÛñŸþóÇùãüø¯GûøwÿããâÍÿ€ÊS\÷¼jûøüó˜íG¿ÆG¿~Ü­}üùqåÇ=jüÌ‚:Œq£ ^?ÊuýVpl¿éÏÞûïë7÷üñœå÷ýäxKÊ5~”íÿñãßÿû¹Ï·ìæ‡Ü?z½¢à}ÃÕ~”³~Œñã|o| FÿQÊÜ +æÛ†O?Öo\‡”ÿZôê¾U"JV-žö£¾­ºj± +¢ëG®Æ*ø‹õð£·z¸dÕãzÛ÷ì}«È*9\•ígQ“,ø‹5ñ3·š¬÷FMVÝ¢&Y²j’?‹šdÁ_¬É[Ø'5½´žà’÷½åù1Ÿ·&óùÑ^]Üj’%«&ù³¨Iüµš”rýxJÙÚ$JV›”sþ˜mWÖU’5Y?sMVÁ_¬‰Ÿ¹Õd½7j²ê5É’U“üYÔ$ þbMúù£Œ/5qÉV“úŽ×§í5É’U“üYÔ$ þbMüÌ­&ë½Q“U·¨I–¬šäÏ¢&Yð5ö½ÿ¾ë>v\²R~ÌóËØÉ’¥±ù³ÐØ,@M4Ÿüó?½¦XßÒƪ¸œçÍ‚Rïw0°O!^ÆÆÖ:Åó5NŸ(¸Úõq”Ö<ûÇ]ðóW„ úÑÏ”>!Ÿs]í?æõHº!µŠáM7÷WUÕÞÿ?Þ¯¿U¬½o|ÅùNL‡žý¶`©,xP£W®7äùþ¼àr¹Šý™ºŸýú<íxöŠ÷Úæ.ÎûµÏŸ«äá‹ñø!%h×ÍêLi‰jÓ9‹”×b5}é}Qã`³ônQÍòœ#ä·I¥p¯TK6ù+ö»¨ÉÇã‚2ÈÏåWáCnöD|ï¿]S<à§ð°`Ü°€¯ê•·å_yŽ r=5ˆîkèÐaŒÉ.ž¾þ¿âhwŠŸoË÷ãÜnè?Î[ÃýR¯ST˪©¢ Ÿ¼ý:Š¯”Ùîïg¾-ÊûËÕY0+õ†ÕEÇ^—ÕRUåû>Tð6 7k“š ÞþX¡oµÖ«~½°`RÛ¥»ÇÄTøcz´¼âá®»·Þ¾£J)ÉTeF|êÛòVi¶äÛ3çuQO6}ÊIÅøãøm|þí(¯Æ•¡OdÑVp½ +qt˜åWÙ4Pî +ýy Æ[Α6ñ»·Ûó› =e¤øÞþιe]¾ÞyÔÃn>ÙIk¾%5ÚãóUJ?väÕéK-rëfØ-š“wPþ›ÅU»Š +*&P~¡¬ËÁïœcŸWfÇS|½øÊéòÒîðƒß‚á/€“v\O6 >9Å·µË£ûYÐ+õñgöGüñ{Ÿ½ÝØèË„Ú¿ÝXe\ðsÈؾåµí·‚c·ó[Ák¶Î.ùy +åÂ)£tuÌIítÆ­å*Ñ-g}ø›Jçù­{÷}ºrÁ î¯þóÏœÇ^×Îùgüñ{¼­òªþê½oèíG{×?À.þ¿ßëµØ¶\öºkýÑî­®–ó-ñƒíµoQ£Á¬l‚??¶‚:/Š´å•å¡’¯4Øß“­.Ïþ6þ+ßM¿†½|+p²õªÍg­¶çy½Fõ–õçÓÝ®/ôÛ+MY·%¢îŸ{úÈ¿~yát/¶@ùöBeκ~\Ö½¬o}¿íX7Ä·ûçnš|z´œ_¾Ú•u£xl•ÿ¦/þ“'ËñŽ«yË%‰ú?!Þ¸›ØçÇ›nÅ’òj'äþ¾ÖÿýY¡4¦o§…u§GŽ÷B{=šŸ²ËãZ%4Ü0c˜kšùqê™Ïä^%Ý—'l܆ƼÂ4NÖøk)rÊ¢Äõwôuý|ÞtT8ýóëì˜À®á3Ï帼ÕÊrkh€ÊæÆÐNóz½áßL9½,Tæ•»æÃw¶…ëR*ÇÏ+ÞDcòçÓ$j>|ksä +S†âèé[@„ãËr<*Є9šZ¢ Ë¡—?Õ¾ÌÛ´¯-© jÉßùrh¾y¯ÔBåygš^² ꃮ,t®èŽâ8½–ÊsŸ¬Ý…·9¯óŠ÷C5·Ú~ÌÏôÌÞô(žÕêA¹]ù.HÝsÁCÁC=x¦®÷‹¾F< "üªw†ÚJàÉ<ºŸk—¯ž¼ìWÅÒó–Ý,ÌàM~•äÔŒU@W~Xç·B#.[xJy¸‡™f¼Ž-ÿ°¯à–Ý"ðmy{†ô´ðvö4†íM‘=~¹›— +§:ª¢öãÔÝøµ›?¿ëyzJRnªÇŸt æ\ê’òkͺ֯z>_ Þê¨ é%\²PÈBñRç •†&].ÂóÄ +?¥ÀÝ;RþääÌè€oxçÃ6õtN‚ŒC°6…®XãDˆ‚ îç+céRèd%ž\9~fúlðyó’åÐë*7Ø¡êÌ)SH#P¹£z*?n”é~!Z—×/åƒvŽMë7 ¡ë–vŸWÆpLa5Oº¡ì¬KÝÉ.(iˆþäëïžhú[êÞ¨GoÓ¬±ÈÊ¡Ž‡JÇó'ÚJn¬z¥ör‹µߢž·z^ôs_­·~½Î·kð :³¦º²‡Ü¶¢¿âçA]ØJl-ù:TVÑ%öÔ£Žá’ 3`“ž +µwF8RÞ;> +¨GüPŒJxÖ˜ÚªŸì°ÒûÝåK ŠTóÛúŠ• >=_iÉJtMÍáTëÓu.÷þóœ«Kéáo;!•°0Z=ˆ‚“ƒåg 50å—Sé/‰C-÷LVjrΦB,bæ(ÀbõkÕt!½˜Zb‚ÀK¦‚ è§î-BÚRƒ:uû|¬ƒ·§V`ÄÝ~uX­· õCð•;§zk…¦ôMËn[!YÉòx]×g@Ô—ßðdø­ìÐ¥µö+ÒùƒM¾ iätìëWúã§Lâ;‡Îª›&qøõÏÝÒÝß^ï­7ÿø}0¾ ²¦õ”†E[A{Íú+ögù +¯¨x8‘~¢ÀþÕðŠ—•ìÑ‚?ÆX¿¼|a +¯ïóŠT|¸,ïpÅâF¾®<ãöçä Æb†¶î¤kŒ•áãé’úÕ §é“ÝQ Ûïåj†q'F¼Ú¡)ÆÈŠ¢œË.N,)t˜^ñÒd? ¡¡éí©®&{µ5¾å¾ÃU8Øus%ªŽ(@;w{"ï}ÕSÑz·3nEÏå}ZWÜ +žÇ«§¯HMBehø«cnéêÖ.Šoil¢C¶_¾yºèdÀt ®žÐòï§Üm›*½¢c¿óJ»¹¤e +¯šîZY:(ìטfàEI<4¥©©â†‹~ÄÑW¿a­‘ë#-ß‹fDØÒJvËuÐjž35”‚n1úE3/‘Ô_©äkQßïV+žRöÏÕM§‚; Ñq°Ü]÷Ëâ¢Õ»Ô^†{…J +ì¡ÿgÜÎå8êÚ±ÑPì»$ŸK6ÎS·{vaÝXõd£nêêÔKsT¦ÊÜp,¾UíجTî’µ»ï"•ô²vPeÚ½Ûò¶l#hɾ,)âu‰ê&?Ž× ­XüC;Ä›nF³çŠù>c3MíG}U¥È³µþ*×ØUQ%üúˆ…c•j†»È±[6¿JU¯ô  Cçr¤DÇæT»eqÃ;`ºÝÉG£©ûe]mÕíŒP"=`þW ͨ˯Fûµãq´¿GÞZ—AÒ7kM~;§cHˆ*Gw6˜õ¶õç[AóµÎ­~™_±{¤­†ãúk%/Gi*E:T|m} 72ôÔduYÛ×!{Åq†®Â"½p1ÂßEøßG¸¯Lk+Ç…·_§þ˜¸_ñ¶ÛŸîjÈÞ ¯ñh/ÚÞ9™öpã;µê–åÅw¦•g«ôº¯ß’û\f¾5Ջ뇞­žá„Q›ùùº7¯|{Á‰pÆÛ§§DÝ«KÙøæ©ÛáŒWl2» +SF ¬×­ª1nF#ÿ°ƒm•a/^QNXù¦Åñ‘7\6•]­·vŒ´Ûiwå¡ÄFG5==•—YWèû{9µçöðnÅ«º[Ê[àhéõ·â…îÍ°Ïû)´ÂP¦›T¯åä·ÊYeetÙ]×èFŸ]3múÌ•ñ‡6»á²%è/X—&ÎŒ|f­•šïhõíQ'o¯Y®uó|Q`“ž{ÿA<éØü¿þÎ:ÏîÂóò†î}»ÚÂ#¬_?À‚Ðê£ËrBñMÙœû]›³Š“"æÔqغ?Ù +J`«ÚäXaèè†ÄE&KÓ·«_:Í$ðƳ:ÛYÜ‘ò'J8VÖ +uúþw6 +Í mý­,¢DZ4ëlFy…¿7ä’øsé”®Fˆx`N= +Ü5mÃ×5‚ÀMaJ4%Ã̈²­mgc[_Šß¼ùßtäoGoò…R“Þáv4ò]ÓBùW¼îÝÈ☛‘ïöÏmÄäo»‘Npm×á/.#ßá 1òà‡Íeä‰/#o|ù~9b%#ß…]¦‘›Èav °{/ˆaä!Ò¶žÛŸÖ,#GŸ=Œ<À¨k7òý¨j#ÿ~X/iÃÑ*Ï#0¨õý†§]iä£ÕÓÈ£Wêfä_™kSù^dÕ½ãÜyþxùW,}ùÞ䤑G×eäQÓkù^MÐ é…RùnÇÊF>Ål×U½&0»‘ d]dä_QAM×¥¡._üoCó­ÝÈÑßóÙÆ)OÑíûçGHm_‘Ž£Ôö•<ÝKkÇ¥Xˆ´ö€ü<+Z=Šb ©¶£¦bòzÕ<=mcÁy2:8šš‹Ì×`&\¦×KQÓ| ºa°‘×É<œ|íóèÖÔËÁòaúÖmowC´û²‰ê#J°:Bc´ ÝÁ‘-#U…«yÔ¾9˜ýº1ÃîM„~¢qÒÔ½µ¯õ‰é0so³2!3·µ»¬Ûýj=ͺ鋙çP%]VÀEFî»<Š `se—Š|ü¢-PºqòT U€Šªž2™˜ñ!öž3 +?*Í~yôM§ŸH2PðD£ÞArŒŠQÖ’²«Iº©Ž±\Å̈úöÐt¬‹·ÿÊ°19ß(yûø­C¢׈\Ž0—æŽEq«O¨#‚oï˹ö5’µ mF(ä#ð]úëLŒšÑ Å8Æ×È+ž›7C1ìî>!»û.ÞÇÂ;°¾ý¥[¿íéâ¯çâ§'='×›¶‰ +(\â¯ûy¿¾k9ÂP”ÔA—€ø(H D- >KˆÇo@ß2Ï7ˆ:W%iò2Ï7œ-xȨ±øˆÏëâù¼w<ˆç×c!ñ¨Œ«‘v4Vx)GŠ² 2\c$â,%‘øëiö–Ä£`ˆG‡ +"†FâC\H|Þ ä›¢‘o|ÛÓ1ˆ§,jàYŠ%pxHsß×3 +Ád×GA±l»- > +²:'›Ãà8•gøâQÒÄJœn\Äk¢ÍЪâ& ‰‡L28E±ÔÛˆL(ží(žâ,ŇøI͹¶Cñ|¼(ŒðµñvsLÌÓ} ª°tjÄ@¸»|¶®‚‹ ,CñIènº+H†âÑ8ªSæt[ÏaüL ÔÓ»Ϧo%xŽ± +ˆgGÕ@|vã×nþü®çˆýH$~)L4ú Å) C²ˆÅq8ˆ‰÷ˆ*Ê ÚX<õ†VäRH °Š9¡ø¼AP<Wï€â9o#¬˜M¨pµ$™Ì‹cnYpÑ”Š§r“>*Š³šlƒâik°ªOKÔ?ŒÄ[J >® +‡§Äu…pxZ=ašÂáÃ.‡‡HŒÊ}ÚEB5,¦(±ø(0OõgÐÑOÆ„â9HÌY1?w} +XëJõ-=¡x*¤ {,‰)^\À«¥lHŠ…5O‘Áw«bÉÄyƒ‰Kèʆâ9z„’ +‹ç$øܵsl’» ñøÚ󺡘¤Á?g,þz6ÀmšÖÐBp ijÕãâÙÄÃÓص+`xöFÔø:Ó¸‘{ðœ«;éÝo;È2¯ùá žD,qxv¡šS8¼úô >L’qøe´ŒÃçø3Ÿ*ajÝR1Se# +ónøðÇ~&¯F0<¿¦]ÃË ÀáÙlãðÔ08yÂá:g Ø=ös¨kvÿ¦à”«ò¥d´@¼\—@üú`ñh XW#ñôì%‰iM Éø(H >|Ãâ]p,$ž}Pzñ4C÷ ž/üHž/¸JÀðvÔãðË‘3 †áe/gÀðË Où9†ç«…²ÇÇ*èež>(ÓB᥋3Px¶¬H1BáÙòOÂîì\(ÃM²ˆ‚èË?~Š‰ÂKeêBáõØ(|¸ Fá!ä´çê9Ö(̼pø5Ôå½GàðšGZàð|™Þ≷rטf´aÛ¸ +ÁF f\ãðÙqÄácu”8<ºù!^Ó8#ÈGàðœZé ‡§–´;pxˆPUñ¡RŸ«Ÿ÷<ÖèAÈ°Ï9]*'ž*\ûê•KØc˜†ežÃk–@Þ9ϹDE¦>÷¬bÃk%tOË©nêîÔ“Óƒqxš#ýúœm3W‚áÙ³úOo°tãð›)où¸2ØîO_–T0ü±™ZãðyCq¨À?¿=^@:ß~i3Źl­qxV¾åÆÓTËFÃðvÀá—_`>üãðá„¥_`þÈ„Ãóç <´œÈ­âYg~lV™¬óa¥7$~îDâ±IM÷ +_½ÚŒßdŒ½¡õ0ö-|Å0ö@•Ç#‡Ï€éå%¬}[æÚ@G\XûV¼–µ'tÊ•Ôe¢7ÀVOa0÷€f=naîÛeæˆx»ÂÜDæ=rY¹‹Â2÷M£&Ì=ùv¥ÕØÄ&?s+‰$Òÿ¤½o—'ì=êç•=ahñ›Ž0èÍúœ¿Uûv¾^í9Éàûmm3øÑß_ˆý¿ X߈~ü¶‚FŒKóx´³®¿v‹¾ö|EmíM1€ÉÍ À4 +@OÛ +ð¿Ãä£kûfñ²Û‚ÃⓃq…Å?v¹Î°ø½ŠaßO~aòÑøï2ä°Ícàaó!*&¿i×QØüæ5³m>´¼µc3úÙWÜž‘ƒH6¿­Cc-§ÉW¿ióAŽ_M“Çl›q–´ù)Úæ{ .¶m~ÛìÊi®F:Øü¦ÍȶùÉÍXF¿9œa›Oµ¼Ó惲pîVm5¦ìÌ6÷ŠŒ6ûDiô›|´¼îx@üúm“Û3N²o¹ǪŽCôN«ßNm%O«1{¥Ù'ÔîØîã +× »¯1|mf¿•X|ÂêC©s1x¤¥ÕoÚÜ­:ØqÆkwÛŒ~cXCó3¬þfžƒ³Ykò5~5àdWGmÓ‡§šðû³ 3œûSœ|刟Çþçø®>óöß +.=l+ÑR¢ã/'Va½µ.Ìœ’9Mî|Í)´‰8½µöÌà¯ß¹˜¦f߶ާƒ‡I#,Á|â\ZŠãû\¹¤˜p©Bſ܆<—&€–º 6Î-ÿÝ KU"kß"ŠÉÜÖ¤uéÏÆ—‘†H±{#ÅŠ¹¿¨ÑÇGêõHCÓǦ7*øãwUzµëáÔ½â¯ý´™økD¸µŸTÈ‚»—{Â3Õ¤¤€M„_ûKwl·qå$Ù’Û£¯@Û﵎¼~yYT)zÝ¡à+Ðôª…\@ãÕé^|M1BpY à+H +Žn`$›Ã°_ö»—|Sà +ç¤<+öÚEþŽ«§@›ˆ½"çÂÙ¶Ø+’ Œ;c¯í!.±×è§-öÚåg$ïßBsý² ªÐ+).O¸ |¦D‡^‘â¡JƼž½¡×,pè÷ÏeXɱËØk2½âDL:ñ¡Wè@‹(Åé0˜æ÷^2.ö¤`8V‚øÜ+>ûf“ˆI¡W°Mì‰"ôš"»Ýý%½öjÔ‡áWHR?Ù!ëÂK— ÑWl_v¬_ó*6äÆP™<¡²}ùOg0`Z.šo~c™«û¾ôîwýMN替»胑…³Ý«"QP…lŸ¨•û.ÿÛ!”½gFƒç%¶ÁŽ”Œê×PEðc\¯Z­ÂõtÀÌcyêoAõ²ÿÑõÖUùdž4Œå@ò~µw¡:Œ>bl¢pé?zhµ Kðz£eØÒt„*…äh½ø­}Y5;øró†K8 Äw8¯\èu•­éE}-Ï+C¤àJœþöS·–~ÇÀ§AËÇ™S&W`4À+6Ä7S.Z•_ô…àÿÉýaK…²@{&ÆÉD%\!VjT<нp²W{ +”0’s¹—Oï²——!Ú{¢y ¯Ló¹l±<$/’ºx\.`2œ½„hžŸº)CÔÎEñÀãû4Ãã LÄ‹‹â×Mñ§Ó xàÍï'}Añ%8\£Ûê½D…Ãc8Èc†ÇèдÅðxe„Ãc z‹ÁðxÅ~?Éð°¸1<âS*†øÓÁð€Øœo€kT\OR<Æà +58O§­`ÇÝŒÏ/’G,¨<®:6’‡ ²F¤uë<ÂË|îý$ëÉñ‘/ïToõQttq£€vÐÛü=÷%7Ïc(¹ìây¸©ƒçaW¡Êq[®Dð<âºùôÄKò<èiÇÞ÷[®É;¬Ï®Ç¼’çÚLï­×LJ³Døç[³o¢GúºAô°?DÛx=rˆ„ Ûõ˜S¦²lèwÝíea»rz¬9¦Fú=Lk²¼ÞèYÏ#:N<¯‘ÏÝüú‘oÓ鸣‡ZÜN¯h·¢&Íc(¸4P©Åó`¤§¬±#5xÐP©œyÐç{f¾jüõÑcó<0¸JfXàPìÏ.öâN+Ýëz=hAœ„£z¦éæ…Ô˜¨úH¢ÇŠ –¹rÂZ3eŸ 6<”ªÂDeÉÛ2WË„ aGpaÙÙ zÄuãþy=ôø-á_?F=diƒçáM(Éóà$um<-$zØW žGºÁó°K<Ïø©Cé‹×ðÉ|ƒyÞšÐê½€$ a½…¼ƒcMqE²àÔRwÕ,Öc=„ˆùðþÙŸIÎȨ6Ò ¬¡±mŠý­‘ÿvtOkH>ÌxzØ?e€éÅ 1E5{Ìb—gØ+‘SºÚC¹õi‘¦;R¿¬«¾~ZS´¹Ÿ©{ÞÄhÎXÉ-_þZ»=h]^n4åaŠeV“z„óýÊW—óÍ׫Á¹x<­J…6QMÇG,ëðøëñ\ÐX›ê0 ­5*?¼¤ 4Ö‚MSÛ>ÍíºÛ~ÃS{Øô—¯j +°Å×Û-°…Ѽí}=z®{Ò7êsÚ{Æ* е–»¡]ÉY>cÓ/Wfx{µ7¨ +U›Ø´;y_'“M]O««£O'(ø¹0oÄãySˆË¦äó‹ã8(°ë×Ù´2ò>ûKy›È[ïUÒæ2öàÙ¦G¼ô²G-}n[TbyD¾m0ûeß.-j¿¼ìõ +Å,ÿ¼xäãK>€¯M¨^çÆré·€ +(‹qëVnç_½+•.ÙiúÑ›—‘N™øH­zò*hh¯ØµHCåÆ?÷ü ÇdÏ*©9iʳêryAãš~ýo¿( Zw¶èÏ»Þ:Þ#dñŠúcË×™9ª?vP»¥’)¡¨YsÜË6.Æ¥C¸y] &Æa'h1=EŠ{Úº]ÅéÀ°»A†ª +Ýïgóéò°·J´mDج9NoªÚ8Ax«-¾× ¥Ë4€ˆ&à©a„…ƒf)êé]Æ·5ýºŒÁ,k­–Ùן„–¹§cÚ2çõ<¹H‘³áiÍ ÝÝóIsÞ}˜ó· ¹àÑõˆO~~”ú‘«Þ®° ­ùÒó¯cth7ïÄ£FmÄÀJ¢gŠêè—£oZº:ˆ‚4AeXçd5¨~zš…= +Ìá”]z& ¢-LêhÄyR¡X¨”#âd™¦œj +ä +3#‡<6Ÿ×žÝ(øÍÆ2*yöå4cÕ8Ó .Öé&ä8 ¬§tÔ’I+¬±V³Ì>py.{Ÿ{þ£`¨ás1<>~éÕo;zÁ€ðm™-y+ Šb5';ÅÎOL ƒ;a@,÷Èy"è€ @ÝÒ®GÁ«¸?Ûnï(I °*G@µÄç*iκA<àY»½±4¡V + +´¸ À¸n(°’-H ?îÚ6{;ÿ~@}X*- BþUsß ¾âÙÛÞÚIÆô³`BhžûÙ€@dª¶›ãBìcµŠ5@`Õ2Ý`,ëZª“ß³À·€“œ‘À:5—åÛV^H +tf±@ˆtòi <òcxþy'X‘,,°"åSB}¯Ä Åc׸¡+¾e,°ŠIX ‡å±^Ÿ:ІÁ´Uo‰b˜ë±ÀªF`U KŒbˆµ8І۽ÑS5ó®g?~íçÏïº~aV…¦ÆDŽ¼æ†îÂët.FaLÓ"g:{^ „Î_™z"³Ð ´¼°À¸ÁX ”žGÎ ¬RÌBçààØ÷öjã‰>•wk5N¥’¿Åxê“` Úlam0 ¬Jß` ÅÆõy ÇL0°ê˜ŽÚ8Xµ‡"ú«Š×ö ´MZ`   ĘÉŒÇäÊ¿ÎaÁ4Bkž $^h ô²lã`à!±”mOhµ +4bI0PR`G^6ˆg?#¡@Œ nؘ`ôîªL†‘¾÷®×uú%³±P >úl +¬CÝH(ðP#+¡@4a1¶ÇPKu.;CÕ9Ìk*Œ²Ï#w~ÛÐ]øµ‹w00{}iíÞÀ@Ø«sKÀÎ)#Á@˜ë9 Dãõ=; +V} ¬‚ ÄH¹® + ŒL»Vm*1ˆž D‡``UÌ'À@[§Ó~%¸nàRΚa,p©™Á@ÙË«–ú 2XZ64°ÊÏ +4°jÉh ZâÜ2°Óå×Ú[¿éUœwÂnÙ…ºoØ÷KÁ¡öW(5n‘2X•«$ðÀüâ í\G"‚U±”«¨» tÁí( x|-¡ç>¨‰Â=%ñÀÊd)X•ö@Ð~›÷~§[x`\žënedç"Bnè± Áªmn±ùoÇFó‘Ÿûh°¬Bñ„~0UAA´îÛ¹­´ŠÆüÐÁóÚÅ»l ºóø‚ æx\˜`m.Ú +¸ß˜ }ˆÀyW[˜ Æ/f£À+S˜0o¡µ­# ÁÚIs1$H¤äÚ!Áª Y`‚5°"¸n à[0j¦`—)}&X{TƘ`a(@@ÎÊugX˜ ,æœ VíI L°*»äÂÝÔ ÚeL0]ŠÀ㺱&ˆÚs/àæR`a‚ra&äUÔÀáðè,.c‚éö&˜ í¿1Áôz´˜ M½1Áœ \×cr!1À1q¼«Ž +DejB~Xá´º0AôÄ ñT×t%;çŽ&¨ÕÒ‚ë o'DPO»ÎjÂœP¬:’4@ÁªXW€‚Ò)8;ÙQ=t +ž®ÔÞk·Ñ +P*\ŸÕ-*x(¸Æ‹AAôÿ\»½«à“M¼æ“   DUF€î)†•3(Xkô“AÁª˜N€‚µj8z÷·ìÕ±‚ˆ­&(U¯ .cÞÒ:rÛ¼!A™Ò#1Á´µ º 0Áø¹Ã¤ëñ΢ηk³ø¹lm€‚ < 䊻m  V‘‚í".H0ƒ€í$èy¿.§¯X+Â7ó +†ñ; +®˜É­½P»óZJTÅýÅDï`Zí VíNøû[¿ó7[,}lý¶¥­ßiésë·l}ný–©ß©»ÞùmS;¿mêsç·ŒýÇÚùm[;¿eëcãwÚúØøm[ÄÆoÌØø¶>6~ÛÖǾn›ŒMœò3³Dæ16~ÛØÇÆï4ö±ñÛÆ>7~Ûš¶˜Ö>6~Çuo&¶µßiísã·W¬±ñÛÖ>6~§µÌ4­}ìüÖç¬ß¶ö±ó;Í}ìü¶¹ß6ç±ó[â¾ó{Ý@ØÐöÞ;¿ÓÜÇÎo™ûØ×-s¿6~§¹ß +˜bÀ¿£ï¼ñ;ì½÷}ÛÞ¹ñÛö>6즽ß6ø±ñÛ_¿—±6~Ûàç’Áok¾ÐÐöÎoÛûìÛûØù½w~ÛàÇÆnüM”ÁwÁa‹Ÿ;¿mðÛ²+6Þúmƒ[¿mð›wò/5¶~ÛàÇÖoüØù&Ù;¿Ãd±óÛ=v~§Åßq=¢Þs‰M Ç{P†s?µSüòÏaòcçwšüØù6_¿?ÂèÇÆoÙüØ÷6ßû¾mò½ïÛ]Z´V~kßwÜà}߶ø±ï;-¾÷}ÛâK ÿÅTïû¾7‹ÌÖ³ÒÉÍ7€ó½Ü1©aáþ¡æ#Pk ƒ~3‚£ÈÏ$Ÿãں˻§Wwa1T×õiç¡èŒ©´A|[á7í†òL¾‰côˆg´¡x+U±év‚Ä +âØÌý¢^Û¿¢Œb‹ ƒLr +ì~í×a¨1}Hyz|H¡Sä«ö0#±«Þ+=m á·–°×Gîô^ý¤ý…1uî3¿çDq¢tM™iÏa%ž´ç÷mt_-9Ù´IcÓÚJ +äm—b¢„ü +â +4ß&~Ê&â,‰ÏU›× ç_ž«jäÓúê)} +~®¬J(u-†›©DŒ<4_ÀÃhÃiv–°¬” +R;Í]à…òt™PÙ4fA•Ë)ôpn‡<.qŒDzn‡z.bÚ¾!Í€xWDõø'ÐE n|h ³ë¤eÕƒà#ÇQ„Å|àBN– a¨NšÐܪ¹Ÿg=ÜÑ—lMYŽA4‘GCHTìOüÜ øƒG“q<îÉÙ÷J£P²6:…#«úõS¾ýº¿a²E¦þüȲº7"q st€Rð7ή6­ÊÍÿ,ËÚcR Ëë +×ërÄcͳáø_f†Gc—´ë=–!•­ÔuðÌêï.(Ds9l<{[V´s_x,K.Ÿa`)ÊRJV}„A1NéE>õ ŽØ‰;[줢éÙÞî-QkOPAÂÚ##¶È_Û§/q6$ñz‹~Ù°¸ßúŽŒ6šå¿·¬èǤ©5yä ¼²‘V`u‡¦”Pì<ÿ¤$…7;œFÀ¦\†²°í“®e]7 ¢¶¹z%wÿ Šr  áÐZs6L©#>€Y¡©z@CJ?ì)¯kôÝf„pOl%-lT×­³_7ѳP–ˆŒÇ9x‰ Ó¨^¥Tî½ö–Þ'gpbýÝvÙÚ&Ô‡,‡åQ +S49÷6Cnû˜¢ÁìñPà +ºÐÞ"ÃÃ.œ=»¢ï¤Ÿ0£CNÑЄ~务9úÅ!wéQ”]—¢sUƒ?›×ñ|õÆÉÞ峋FrâœÅ"9™³HÎG»€Í"±˜,ÉÙ8ÍH3‹äÔQ ‹E‚‚Ò’EòŠTjq4 )¾à‚«p¼/I,*ÁÙÈ•[,dø¶×uvl·3NÇÎM#9µÎ* +”¬ÑÛ|·N10ä- …ŠCÞoଯ{¨I‹F‚ö&åZ4’ÓùŸM#±¸7˜F‚×+i$èÍb¢‡”û#Ñ4‘ÇÄ\Ç.[;¢`üØÎ 8µY:h$˜‹Fò¶ÍCÌÒ4´Õ9“FrvîtY4’SQ ‘œ~Œh$è²4ôÓ;ä@eG~íèÏïú~ÑH¬!‹F’*çÃT‹F²ˆFò–6cBgÔw8•xiÑH Ôc&ätRê ‘X^4’¸ÁÁ<þéI#Au*ÑiÓHÎ;On§ª½ò)^ˆÄÉ-á‹F‚nÙ€ô»]I#Q›í4Ø›ö$ÖK€?i$$®äl>H€3ïÙÉ/‰­c°HNÈàîzÅq}Í#/‹´8$’ƒB‚% #ökCùy+z’Ó‡«GçO>fQH`ι($ÐQ¥à’3O¦u[u àXgƒCq.‰Åíà€¸ÁË–SC-h$§œE#9½Û4‘Sù6’G²÷½n˜ ’~žYSMžgØr]ä•yz½y$§rß- Ô°Ïä‘ åŸ;y$§œ»GbK<wã—Nþ®Û‰Ãj£œ˜ƒ6É9è˃Z_ž`ÀR];äìÊ nÉ)`!$0TÃzpœ&Æ"œÚŸh ´ ,þÈ©pnðGl–‚?’†+ù#ë†î Ÿ:aÉÒ1Hd)“@BèY’³Ûà É)+$øVQ%š5|˜Bç*ƳUÍAÓ+é"n×Åa·,jȯ¢YÅ_Jr™;r* JpGòcƒ;r6å‰S`0ΙÜØ—Å¡¸x#ö +×Fò,8‚7wê.Ij-‰#§r°,â}¹'7’ËUËCÒ• ∠‚8‚__-‰#é‹š7‚Ú#†Ä‘SÛ‚8âº-âÈIªGðF¨…-i#hØ6÷SÐòmm‡§Xê.¶¹GÜ“ü:g»1FðÀëIƈ݅`Œ`|^F;«Õæ|é—«·”Œîxü­U\¾ ŒœŠ!š2rŽð‰ƒ2r*ô”‘3¹2fSæ&(#¯ÌM´$`5Y5SF^y$ŠBÛÍ9y Æ"Œ°•ƒ.b× ê¤IF‎ëf!à·Ú¸¯©^g·/ºˆ]³Eº:o]ôØÆ/!ÝÛ ‹èSò¬[ùÃt‘ônƒ.bo5è"²éf‹„É¯Ë r2þ˜@uW/¾û˨Þ: >¦“›NÓ&6·Ô*)>I`ôÕe"‹xQ´Ø"èà1ó¤Ì³®“ 72ÛàIñFÿNMÁ‘:}¬ä'W ؇ú+Ù"ç´‰ +¶”ù)«W†´¯‡îòìG`d•µgãp´%v—Ͻ«×<*€“ßL¶ÈÙÝO&‹œŠÞYä È"i£jpjãnE`ã”å ÝË|·eu2ái[Ϥy +®HÜ`|=~^rÝ 8Å“#QäHãL®áãHÏK×ؘ"^)Vï·cxW$|€ ŠØªˆ§øT ð‚)nxPEð8s¶UÂ.jÓßÈ1‘Ý0;QĦzÑDN'Bÿ&r ˜ƒ™z°rjNÍGC% ôö*è®îò ãéÃw²yzx#TÐÄÁ^‘JŸ×Ûšk6À¯ÂñÍÑÛ•êœÈVhìÛvG«Ž¶ £×Ýn¬ !FÙ¥›A\‚„ZéÞ¢tEï±hø*j삃ö¡óñ—VÕ„Œ» ŒÞ¸û²Óþ”õòAo®9…ÑŒHä +],ŠøyçÛóé¡IñòÆ)Ž™œO{07S={ Èã ÑàÉí³÷ +ã ¹„Wá½ûñ̬&6ÀQ?ibTÒåÏ$BØ —‡j#p·¦5h‘½;<_5ÕDqܦ㪷êWzO­ +‡íI%¿N¨XQ w]w[ž½t®ßv™.Dø÷T¢veåØs½æ·%ÓàÖÓ^i—¥éhƒËÓ<UÁ‘FÒ0Ì\ c>w‘ÉË>W‰6½6mV†C5D^êöŸî²×æ&òD (!'—~Rë>õñšÂ`Þ0œ›<"V­xÝ;½ 2« Sæ×æl©;)d$¨Xd?ͯ׵ŒM‘D"‰Ýç\×Ã54¨y‘ó)tôTÁàௗçnÀ)ß^Rl˜ñ8ÇȦŽU`vS«,©ð9ä%‘à\ߧÖm¹hîÕêøzQ„ÛŽLãÑÔŽÞf*èBUÒû䛽óôñW„'{éÄ'†?Ö´í•uŸÖHÂR(¨âÒ ¯Ù¥@ÝŸÊÑzÈ]s(­?cˆEAÁÎTàú<+4®¬);ÕªC˜[©›3ÕaÞ&Ì0wÆ@'µé¬’u kØÝw¹nï´ûEwÓž(ú“ƒ³ÒÏc©’JBÏ,…Ž˜ÄÂ\ +Ñ°ª‘,:¤õ£Ç"Ä戀ˆ ÐVg]/¶…#Œ£vRZÔÐzuG_‡Î‰‘ñÜ%nd×°D‡2ÄDÒt +ÙÖ¹ÂJu °¶Ë£;‰–ÔCÏWæpõ¨âÛ]Ž(RÞÊš¯÷˜îuÈD9!¿é¹KaRÇС5&ˆ“•+VÄwÉ<%öèÚãxr|–†'áqçd#wç<Ô/×ÃW¥ùŸ+˜eo…+8yñT +ÏS„ˆ5(‚ŠÆÔDÍÉEÝ|DP‰/ +>쟊’QóÒ”;IQ‘=¸“xvÑÈ¥óÁ®'¾ÔÛW½°;£Ê[âDt3 +¶"Ð ƒŒ²®?®ØÃX^¶z 6‘=æ$NL¢PñºjiAÎÇAÜî=ü¹Œì§mŠP’ö˜€öùaGó Þñ|^UúÛW’ Îs[F¶HýìËÓÑ­[3IˆÑ¬!{„ÎŒQ@ûЊEªt“ŠºÜ©Ë^Ð8„úô˜§@âS›t¡m–+ä‚ÕˆD]¦ŽõD;x}ƒ2ššbyy¹bûõ\OL6JÔåtÿ†£ÁÃ-Nc€¬ +û/ê"wùßÆ%ùšçÔT¤óŒ­àç*Pð +<“gþ* y‰û+€—Á5Wwò%Ìô“ˆ4ƒ +Ïneê=ý[—/û¨½H:vs`´xGÎôwûˆÆÕì^§Hüã÷ï~›bxþ÷ÖNÌ„¡³? ÊgàìçõŠ¼9ÞÈLá@5uìÓŸ[³T¾>à÷×奙1Ã-}µÜÌo£ ù¼–È ü¼.·è”ëq]ýéÎSo7¯&ê¶D9ô[A=•®>~ÎÔ÷n‹WŒL)z½2gåN¾=ê~ +]þÌOóe}xþx5Œž½Ú¯^­ªš}möÏïz"Ù sÒ”%9aJC—tÕ% +GZÌL˜ ÛAL˜¤ ‰–0»Ç·Y –“”0…‘áÞò±( sÄI÷ˆšOe¦!a÷¸ùx6󑨒=ò–²ÅgD‡LF°l.Õï "Ìø.#ºsˆ‡f¦ÁœŠµZº1 Ù40ÑîSÝ-Â+ñ\Îd!Üì© !Ü蜠 HH‚¯ ë‡Pá’Š}p_ÓL>¸/† D=¸•°M°>Þ7ÊW!M¡!'ð<å¨%çÀ²«1ÍØ`»è&ÌéÔ‹âÌ)œ’/žÓþ¨ë¡’lpŸÒkÍùÈäÐ{îÓa3 + +ˆ·rñ ù±fHÎÄõxò5ƒdðJ&Јc0ÍɃ`> +%[ºutÐçVŸÒƒy‹g ¨‹‡VÒ ¦{ˆ\0¹=‚\€‘¤ø‚sD¶ô&¹w3 æ0>o^ú£1e½êïÎúÒ•Ÿßtn’ +ÔÿI)uñ¤/•|‚]Æ|q/G° î‹ +“ îKЩPÙRƒIp+ !"AHGòâ²hÐá~‹à>mÅM"¸Ï8q}7Á/_‰)Þn1߀Ҥ0}`ÎHb“­eîÀĤÚàLy¼¦Ìð͈«"À*#x´z0oÀFÌ´iJƒúÖn(¡ˆÑcYE°lÒÔ[i$¹õ¨JÊlÑÙ’1pûxu.ÌVóÍEŒoºÀ*e¶¤š\›¤ +H +ƒ)0…w™(0ý2O`:ÆošÞz¶` LƒYæ`Ø\W0æ­ú)1_ˆ™#ø‹´ˆú ÞÜ€éÓɵ†¿y&”xÍè)â@³DÔÆUr +áš^gš¤â +¾ÑI_ºð»NMJFKŸ‹ërmœ€©È)SŽºh¥16FìK›A˜¢Ã›¥çDg:À«Òl€É Áa2$ñcD@×ÕL,ïc*À>wt{ÊD€Ð ŧ–þhyk³f}a­¡I=âà t3àÏ's:l`\þ +íÀqm†û݈ ÿ» ÞÿU¦6>K>æ$·Ùð?*zøŸ_hì>ÍÈÿ4Š%ÜöAC#ÿ*Xп}¯Dþw¸?šZ ãû¯XôŸÁñ3æGƒ±$Èß‘ÿô–Œ÷‡3e¼öH#!’>žÐþ©Å¤±þ©í‰†úYŒSýhˆ9èŸÎ‚#œ={š€Å8zSå;'ÙÄGˆw7$goýñÛJ”N—,™˜±P~OÚùgâ¨òoQú ñãíO$…x¹ø1v…¸"šôJ˜ŸïÏ[~æè>ÜŠó +pêcû0q;¶?oo‚öañX#!ûó‰J؇÷rn?µëÊÒ}¶r¿G€ú·Î0¦_†Œé»Q +ê{ž¦oáóH?® +*¾u +¢ý;V(Æóå-Í¿uŠæa0ÿ¾l—§óh(_õ$_–Y0¾=Ç#a|»‡Fñm†…⧕6Š¿.ÚÞs?µAüû lX>ªIß9ïök“΀c!O‹ßGß¾÷Z"ÑûW&ü.ð}Zr“>&ºW_€îç:šþæUF÷R–Äí§¸¼1n§j?C4h?I<ŒÖ‡>ßFì—æ ¯Ÿ·²ºŸôõ—Ðn÷¢eLOÆê1öíCë/´ý‘P=ìé5©‡i=€z[gt€õé%0ú©½Ñ‡0z[ÖéiÔz`ô¶€ÎæöÐ}ØCôñSáóëÁBØñÚ2ÈÁöÐèü”¹:çR³,pÞ‹*cóöÂŒÌç„ld^òȼçàP˜‘ËÇU¡òsx”pU$éHL~ç]V‘€¯6täÓª&"?…þ]@þf4#áø[g1¿•·_`<ÌBñèÃ&åœ Åkl„5Öφá}­å\ P`^ñD6‚Îúpäø[[a ÀÃRm¿Ã,”@ßa´—8.EÀnZ_¶Øú­J–fD¢—Ü…¿ãò\s†p)ÁQÓ™„©Ï…×KªBÍ\¥©iñÓÁ—ƃ”!ÞŽq¤ÝÉ@| µú4æ&Éßh{Fu ¶[5lße«õPºš9¢;N-.j(hº0 [Mî +}cSû¡(NR1P)€ÚÂ÷V•ÿaÈ>¯{ÅftýÈÑõuù’Cvc딦Ái´ü”η+kti~“¥2Ê/›ò{ŒªOáÜÕgBv§²Ô`‚é!È= œ$%Œ”§ðÿ“Žxœ~ÌÛógTÂ@‚ðô©FÓaû…LŸ‚a…¥K1G ,³Èe¦ ÖŠðHŸ‰-©wBË䟭Ë\é¥ôrÝLl3.ÚõùÚ¸è‘3›tÌŠW"èó‰l–ä•[¾}v:Ñs_²„žß§C‚Ï1Ö…þýÔpëåex ­'B;qYÀ¹œãæ”;šF'Eà#_ò<šÓ•qR³ØU„ÌÃí1BzkÃ_âå°E"x0¶x*±©ár|é£üñ4¹„o +·CÉh®rGp(窱“s}òL%.6ËOµ“?$§yºŒ‘Ó%8ƒ<+Y%÷(ZÌ]ÑïXŒ‰N`™,I燑ƒ.V‡4HWC½î;¶1·Ò¯§¿~0néH`|]¦•ôƒ‹ÃñÌq˜r‡w1&©( Õˆœ ñ©…´q­7 ˆ¯Åˆ;{/äŠ8 ÑÜkEMÌœ°\4ÜnŸAËðp(.i·Åé¶Ú­}|Õ‘ï´æoÈÕÏÅk€ +K†+°$‚w–TèW8rú‹×½ߗWÉÍô…2üÔYoɚ̌*ø¨y£ +€û T¡=±FM”khà +í‰õ8=¿¦¸:†Íø…ä–ñ戚òœVh#†q!ã†,ª`Q '1¨ÐßÉ•Êø¶ÎšRtØñ‡¿|"½—"[»êcA +Mû)u) )(¦²£ÆêÖcÚÅ™·%ãЮ‡c%|‚2ïça¨ÀS=Sð‹TˆŠÄÂÐè¾Q…&@  +~Q¢ +¤ +ôá5ñ-½PlO<Èì˜[¡!£ + %(œ‡ÕI›4'‰*4A¸FŒÓU)Q…U@¦ŠWFÚŒ5’]Æ)»+TJPUˆ/;›< +9NIòc`&š;"¶žôµ¥,`…vG0‹Ýõ¥3?¿éÞ× +t**„gÉïôÜÙ[̤hùÞAE?tñ%%ÂåC#èˆÔ +ƒ7kõÑkÌ¥N•1ìaOåÁàdÏqˆUC’ŽV~“3~Ô•úi—»ÕåîΚÑv>/ðZWJ[¬(ÞÉ¢söYãE¾°#üÂ;ï^'g ~™y±w ˆ2!õ’®ƒd‡_ž]Áuìß[‹!£‚&¸Éô!‹ì³íix°.ÃCÓçMôŠÓ÷²êÂÃP36¤<wÛ®½è˜átA–æ+ aå4DSÒzq¤ö0Ç@'­ŠQy¥k¹5>xðn;gÑ­ôÅÆh|Ž1ÜH-oéöÊ:ä)f@W’¤)žÔh1ë©PYM¢<¸£æÊ¢f+äl–Vv¾ª¬^Yhi/f§›–1â›é %hOÞR,¤ÆI šeÐWè2…1/QÀ3ȧB2:ŽÅ_ÀËýª A˜÷¦)Ãß! +sÌŸâーÁ(?àh‘àûÆ/B/Îi +ÞPJ¼©íÛLr*VòÚRìaž©ÃèE´ŒŒkD{‡u-| ØÈá¸ÆT®¤³×SÆZRUD™LZ苲!}Ú|„”Œž:B×ùÌ™äÙûvÿZv—$€Ø$„Jhúšñ¾‚þa)é’“þÑŠ4ýÃàrbD.š¶º™âÑµØ êØããs+€_hŽ1¸ÌÁw÷+pîSËž¥·”¤€ ÝvÂTkŒ`€Àlˆ.âÁQ£²×™ö(( ÑO_zñ»~M +H×s’ƒ.ïEÁBýžA"×i +½7 +HËEš@dÓ?`X¦Q~Ô–Æ»ý£i7Ù4&€Þ’þÑö™þ¡ÑböG;;½õã…ðÞ¤Tq?lÏÌý —q÷µ£!ê«ÄÄâ~xùiîvèù'âh5s?š2áˆêqDë%÷ÃmŸÜŽ_e²¿î5ñ6LqÀÁ´£ì׈ܳp•ÌðpdF’Â8²,ºœ]öÇo* èF–,AO@8a›þÁF•ýƒ3»É|5ŽDûƒÇÓgE…ºXvf0l5Íþ`˜Ê˜2ѹ®,~fô¤ + ,7œ&ì>urš +Ö®÷[\­ ú%ÈVüŽ.¸sIH9ì^"›Ç®ìŸft…y“ýá5ûÓ´Ø9ƒ‹üqäU-èñ˧ûÛ"’ýa_Áô®hºØÊ,ºü‘N£ÙQ±?l•EÿH—Qì#üBÓ?l‚EÿH múǺhcß2ƒCg¸#22LÀü$ߣk/HH—×+K†÷bò‡ºæ0ûÃëˆd0ö݃ýiMd¦>^9ºÏš'zJ¥º…׳¹¨(IüàyÀ=ô:5ƒö…«>KãÄ~wèc¶ú)ê +OÜ ÚGã–ö vÀ¶Î²IWsJÊJ®dÚ磴·aŠ¦žÆðD°>ZP°DûHËbâ ëÙ‚øÑ”ÖB¼ee[Ú¯"2ÏÓÓÖ™ø‘–ÐĸêÔßüið>ô`kB{M$5?l Müà*ö#hN«´­¢Žà}Øû2ï#gbó><›ö¡Ù¶¦ …uëÃEúÀs48<}º¿ý@Fò<¼úÿÅrn´´¥Iûhƒ£úÚGŒÒd}¤Ä»-éuKR@¾f öÁäAWi6¡ômƒã‰è‚ei•£ +­EhÃørXCkÑ÷¼/lðiVŒÃiêM+±ó²qt|ÜFÚªÍhàûQ#Ú`)£ +!ËJÌïmh23+Ú€ïŽ`CSò]‡ÛŽº‚ +M™|ãj®¹lÐI ++Øàƒlh:`ÅÁ†¦±—ц3ÜàîÝ +€”g¨©Îq ɱ;eIFšÔLÑyè]=qŒ5# +Q‘˜:Pm¹÷E{ó1Ø"Þà7åäzŠk=ôÛU²§öNüü½Wÿ K§®#K˜8¾iZŮ٦†SŠcpÐÕ€'óK(œ%`T„®Ê%\šü„{A=© ø¥<&øZx®r‰a¾ÓiôÚ¼*»µg[µãºªƒH“~˜N@žˆ{kΧ +‚Ø­#[LÙ]1ý%ußy ×?“Sî0@Þ/fpû^­€oýMÕ–.I89(,ÞâªÆ¯ÞŒ'FYËnr1Œ·û¸îiUá#”cYÕ.xÓs’d™°”# ЛaÛvGDŒ[aðÞæ ÿ÷Z¶ ÷f +µŸvó¹S^“ªN+Ð`b((_÷š +nºWÞCÞ2FÝôé[‡“éslW}CoY•éÅ·Kk×´ªíÝ:Ëÿð¸²OaõÏzÜ©•!gŠ!“}‡uâ—ÝšëF¬‹ï*ª€[ÃÃ!åª*5Õ,±›ŸºjÀüú>z‘§,€òÀa œÞsŽ‘ˆÎ–ƒ`§Ÿ1ûÂ'¡ëX|ÐÄÍ«ð^²L>™¥ûÚ0ñ`|¹ŠY^’ÖQóùrós‡Øψ°ÂÆuA*áÍrdP|0Ýyw~¿"y:„¦¶|4Êî@“n™¥ˆkÊê@œ45·~*Âó >J5t%Én¤eF¥š Þv®eÃÏs¨eïl¯5E·¥_Ôã}á‰(1§žÎ Òv_B–×è#FnÈX}¦|¬ºß¢7!Þõ€_d¸7o•~YÀoïâ~ÓŸ¡ðdœ‘@ «t%²îÉ]@!S£ÏuÍñN€lMËíÁÚ=C38öZÔƒ>®—´]`]©ï¬ZD:é?$Ð(7anS‡}³6ŒõùD%ÿóú¢ bÎÕè.=eYj„p…H "oJÇßÑâ<ÑwV•‡j¢gÙ‡ºú,uõÌ¿)G’PþîÊÐÄF¾žEBéZÛ˜„‡ù)AB}½M3°écØÃ$‡ ˜gTÊ$”u•¦ºŠ*!J¯:|*I( <µ ¡Øw5 µ`ôÁ<bÕDz8‹‚L”ù$ÓÔ««¦št5–¤¥¾e.ažÃLÐë" +t’þ¡‰(M‡j™ˆbpK4n&J^ÅþŽ­Á™ÅÍK|*x(0&WEƒe˜³6æêÁCiÌCISeŠ'Võ·lw)_Õac.,Ie +ìéÙ‚‡Òžhî+\E†ëÌC¡'4”¦ãÌ’†Bk>ƒ†â¥‡i(ö׎ ¡`ÜŸ-h(²ÑØ6#&¡Ø䎌³`Å!éØz\—áfµà 0´”~E’‚Ò/¢5f `!‚¯7*HçÇ”®ï3…îÎÅ3?‚r8ÚiŠ¡1 "Lj +E#»åâ,kéøWú1(E»m’ƒRN¥£O‰°~JÏt1åzœðÃÇ£8í·NGщ>ëpÉÉA)''ßä HÞ8(×C74Fyœ£òïz›xAôÿ!—)ÎDA†§‘‰ëHÉq"Š6«Ç(¢Þ¥œòÍ2)—ÒD¥"grP€;Öà™-((¥1l}$¥8c¦((ŹzEA±””¸*êGaŽÌ¤ @<鸞V¿{PPŠ2hŠëQ5 êGáÞ–‚¢‚/çŸ<\­ãO$Çé'b&›͸æ$”rŠPx%ÖŽ0³_-¶yÖ¤ò’„Øø +JÑŽ¤#^#¨99(EãÛHwžxb)9(’“ƒ‚'—àÍ÷vÚIña^b™”B:$öü–‰¤\ÊÊ"ŠO{ðé ]ÉBá  +Z«'“P +ã!ÉAA¥Ð-¥ßël“G=MA‰^úÒ‡Ÿßôj²PÜóÉBIMAûEBÙD39j3¥4e%…4í<“"ÈHjÝ[ϽïáÓC€Åe…!¡¿}Cê!6.J©&’<:n„™C,vK2PÈ +Á@A£ëyÌr͘<\KÆù%9¡q|‰¤uz‰¯Š‚r騺8»S`ÝrØ„ÅÑ%Ü+—(ÛFU°ýH +ŠeSPÐ>¢¾ªðhJ +J©ò·@­…ööÎ$|éåÔ"·ŒÌ¥«˜jŠÂ¥ABAEšs– º e¸î`¡à7ý  +žµ½Î´HÁB‰®úÒ‘ßum²P¨Ð×b¡À¸”k±PŠ¶çš…[zÇÑ$h©¹MRT5¥Ð6ÚVL8 oædDÃL”Â=ó‘ˆÒåt<•DøcJ¢Ag’亳ïÙ>‘ÄŠáIR‰DF±m3UjÁE)"—™‡¤È•1…ƳΆ6¥(¤e.JA¥’yH܆ÉEq$×ä7Y¤ß•‡£X|aQ|6N!‰/ŒCH”ßÒd üq帔A`A"9É(ö¿’Œ²ËÉ>AÞ™›¤hTrQ²ËþømX%¥œ.Yò“TÏÛ&£Ø<oƒJ;IÑÎ-±Q ÜÛa#Eœ?³Q`;#I¹ìlNER´$2¥$‹dºâƒ@@FÁLÐFQJá$v˜Ž‚é5¡îÉËSG’€oRÀlæ…ÏM¾ó|‘¢jLF)Ja›d7©É(ž­EF±°R‘ÄU-ì!)Ó §`PŸd; +&£`Þ¯JiA%i¶ÍæE¤÷h6 +ëdg‘Qâd«ÌEbÑd[b‘QÒP×tE|ÑÑ9Jæ"ñ“lTAÄÐ,jë’´tYòõ<™‹$ú†l¯(’‚ɳÜÁFA§ŠüÃISÓIæ")Ú?ê\$E!3R¬-II)%HXÃÞM)™‹jìÄŠ6áGëk¯³X)KõÅJÁLõd.Œ¨ÒS:9‘|nrïAJáÔt)¥œ±zŠcC«ˆSCÄ ó™!a`ÌIýé3 y:ó!il[<ˆ [|`ˆLž9)iã´¯1ã´ÿ´Äçû¸¤â¤Ø"ÆQ!¿>“‘`î˜s±R¼¬2)Åþ˜I)9)Ç1! ¯8&Dópœ³râ«>"ä‰qâ©ôÔæ&¦ VWÛì¤9\›Ýx)iW×!ÚÌõwy)‚Y!ˆ%éOI=afJ„ ÌLqÂÌ”œ›ÌLÉ„™)ƒ03eÅ ÌLq ‚ÌE LLÉDäžWÂÌ«K0SR{ÌL±Ç%©v?^§rz8ñ°£æ§H>‚ ²n‰`¿ù¹C4•€RPµ=‚ ¿¸\Á@)J¶¿$Íž!#»NÐS0yBÌOÁôP";Ç54#Ü¢Á¨ÏôKIO‘|?E«‡à§0º¶ÌÇñ +Sß›rŠ2ßbr<’¡‚u•×÷´ÕjæyfŠ‘ÂÕçÒÃüË»è—fü¹oÝó(â¹Aâ§Ø…J~JYdÆNñbÄO{°:œßü”"*”ø)XÀ¹M~JQbñS ë:$ÄQ.y !ÃãYô‰‡u‡BèÕå!#ËRÄO27’2ÔÄÂ]¦ƒÔ”c»ì Ü¬AMáDlÎÑðÄløòX ­UL±‰ÑØM³4¬ä +¨aÓE.àÄv-Csë$é+ÎœaNmÁž2¿VØ”»Ðe¦‘=e¢ÄMYŽÓÁÛR‡/ºñ²$7%C-f,d0&d¶Î°6„üÇoú‘ô”¿»@Ê —¿0K ÂrÆI}Ê€à‚O@ˆ±V\,3iÅçcäã×áHyŒ¤Y0ÆH2‰÷5+—ñtKâ:ô¶]ez¦¤ŽJë±¢/\گǷŹ\£”ä×,TõK:ÙëÉŠÄk•ÿ>ª¤áîëãø9]™Ìâ[ó§n‰|°Û nîjCÖioa±mô¿çþûÿæã|\üóðÏ; :\`ýù{ï +ž¤„#‚ŸYP'M4"ðʨøkÁ±~S¯ó÷‚õ›>ùýEÖxÜø=ÄX‹GüÇü…A>[çÌß\Þºàý[YóÆ`z¹÷Uæ` ¯äT&Cý&j‘±<´ö­Q²êñ4Æ V=VAÔcý(*²JþjM†Àõ­&.Y5¹¸é[U²äˆÊl?˺dÉ_­‹ŸºÕ%ßœuY¯‰ºDÉV—Uá¨K–üÕº< +Ò½uyT‰ ;gì yËβ×%J¶ºäϲ.YòëRŠ2®v‰’Õ.åT²ÓU—,YuY?‹º¬’¿Z?u«K¾9ë²^u‰’­.«ÂQ—,ù«uA +˜Ù+ú(JVÓ·ºDÉV—üYÖ%Kþj]üÔ­.ùæ¬ËzMÔ%J¶º¬ +G]²äS—벦"DÁœFSÙÔ¢ žðÿ×ùŸÿóÿþçÿþñù/ÿøÿôùüïÿø_Îú—#~1nùó#Ÿ©‚Ÿ:W ÞÔàÒì§M¬Ä ÷ï§ßzl%üöÜ¿ÿ~M@~7’˜ý¯·ôß¾ÿÿ·ã?ýç×+ûø¯GûøwÿÚã}uï¦ÿo¾z|÷Õ¿UÒ«â£\²}7` +žv˜÷±å~~äs²äß/ÿµ2ó›ÊÜÝ +ïqÁªÊÉzÕd*­Çê,øã·§~Wû›jôI€cUëXÓ{ƒ0ÃoÙª‘üöÔïªñ|S +@rèü. Hô÷'\çþˆÔP¾PÐéS_q…a®Mƒ{Ê·/¾¾¼8“oJµJ] +É߬’%!Xr©z|óèo«S¾«Nê€_žŠe9µ&~° +¢*©züþÔo+R¿«Hj_œªe9õ&~° +¢"©|üþÔecþùŸòÕÀ¿0Š+aŸW­Wϯʗ~æX»¬Íù·Q2°cEL™ˆ”8†U¤tˆÊ=Í^ëºaèA Öáß™7>zœŽÃƒ±Œbʺ(…'aaŠºaÑÌwé9|2›Ÿe¨ˆ#òᲈ$ÅÁß5¬?øýµBFR²:”ÖMòÁåÌ[·LW]«ÿURWÖÜß:9ž5Å"½28©”eŠ®üÀûG4í~µëˆÁWR“ö¼›pø'놸Ÿ?nÊ$±@àZÅ;…¬¬ì(a¥N{Óü`äÇ«J™ü@ÈjAFð‘Ù¡tQòHET‰éüxø,Ƨ÷(äv‰Tgø¡’ÏU)7xc{[K]‡ÞeeNßKº5šÈÊs£¥j¿¡ŨÓM­F5:§²U³KÏâÍÇœ§^)%+ïVk14ÏžŒ>%+Í·ò2!ß^¬ }F¯jÀ¨rØ Âásª{¤êÍÇ:° š• Sí+[ïû‹¨ ¥[ºm0¸AíÖóÇ >”*ƒ¡¥êCMY#Š¡/Z%GãÐQñ`ìèqrùûÏåèzÞõCgfVÇ•È@P#‚÷™Ôq|úââz¶;¢¯Ía¨Õ=Ç»òk‡ž)»æ=9ú:éyIC‡|ú z~|Ä ŒP/dÒjÑ.Õ +6ÝÑÔÍ_R¼T2@¹ËÂ1©Ÿeý‚KüuKÏúñ(Vè +D‰ó>Ñ|XŽ¡¤I'{ÑKÆÙ­¬~ËÈŒŸrKI‚­ÚÞDS3lÓn¸½„nóFµÒmó +ÉÝ¿N5;h‡Q¬"fþI‡¦Õ,ø¹ +¦ò¸D,ì×}˜PƯ€­|WÒÜ{ñ˜_墣òüê9³ÿöé‘~¦/ˆ’?¾ùª÷K‘²ß¡ûþ¤§"ÝÌgeÉúxSc¢äØŠV¥âÑQòÇ7¯Cc—H?,S½Ð¼O5Uéê¡R(¸ã 7¼Ï»xz¡R¨Û¾¶mœÛ~“g«¨-¤dõÎNþ§FÙ²Ÿªóªº|éz1®ŽäÞ#ÞËãF¾”œ: cëÛ÷ Oßf‚m.mè€\‘mšf·ŒH£Íá6t2Y}÷çïVÅ^x–áôÇrIØ™-’ß.tS1KÁìY™IBÀÔ¹Fž§Ó³î+DwÒ}ï×y KÊ +Po÷w`™Ûeõa¾]T½Ì³+SÒãš”„§}táf BtË´È)ÎÓîá’èeS sÊOÚáê#"žÛ.?OŒV¿WªC¼J^ÝLÞÐÉ/»µoˆs»ÎÀºK~ýZ.Pšúúác±0ÛÚ?Ò}·Ã MgëÝöššßÕcæj>¦ñ4è€Ýû¼sò£Øâ}!z€úÄ_ç©,Ü€eOE‡·Í;†¿OxšüÚ‡ŒkþT[M=Å®¬…K(뼟ùõ~S®ë“Öµó$¨´õòü¥¸q\h•ÿì±Y…Úë ¸£ÂgkÜ^e€—èv 7¥]oWóíMgQcË0MÄÅ ð$¶-QƒèØJ†9><ê¹×Ó.^B!Dº‚Ûý%g +ÖãÉ •8EBßòdN^å:uMæT®©ÂŒÑ˜*À)lkª0wq›*@P,5§ +RW×Tƒð®±M¤º>9U@¼ï5U,ÙÖ9 +ý¡OÍY"NìJ«oΫ +µj_³D+áo¬û‡ÎcŽëÖ*ÎÛÙ†Õ¤ÆÑÝœ%ZS 9K4|s–0eسO¾«ž$ÉòÛÞ´D€ÿ<˜@"ºŠ³Ê±•0¬ÖOözÕ«tžŸÜ´)fÞÖÐ'¸‡<øá:üút#>z²˜„?ùrÞ‘‹Ð¦M [RÅÒùAbÜŸê]ŒãB·‚õ“çœçB–OáÌxiQ3tè‚˹ú7ñ΋ê{ D¨ï}FéêLU·Ó™§NS;uXØR{&ÓyøöjÛL垤Ϥ5qPv˜Óüþ. •çI¬«„ÇFÁ¥Ü]ìµAéÑyÏ·½ˆGç>©NUG.1¦M‘ÚaK™çÓtýÖŸAŽƒFÁ|4ìeºïðHÔÌ{§|~ÓMï œ¤ô-ù?u\Ê%x+å \ÎjHêÃ%<ϵ+¦’Gi‚\PÅYòkظ¶‡Ö+ÂódŠŸ …àÈÏ핱ã/e¾úaŠßrέõÀ+FUÀÌjWÔÊë¸ +zwº¼ŸÏdÅŠ3®· ²µ·Ï\ŠfÁ¿·³"0Eg°9« ¢ E‡hÉÚüý·B¢]ÇÓ(›¸oÅxÈ©À&€xiw–´`»”½?.C…7±aÊýr?lU%lá‚P9¡´|º¥O¯"z^Å„ÙRUè0Ÿ'V0]¹t&¯“Y•b˜Y3S•pT!—['»)9;cMéÁK,טM‰Þ"“ü]«»,:`8\ü¼ñœˆ|~FÔS +<%¾ï%ëT"J…@â%lîñË®Ë_Ÿ)Ò‚ióSþaú*±ÅûBtõ|0—¯‡j<2^!æÃœdƘ‚Ô<FcA¬ñõÌàa}oQÔã~ž)›—}þ¶Ž×¤–úÍÑ#s)™Þ|†½µáá6HáüÕwOÓÖu•,iÌgHàÑÙ‡ªŽâlÚÊ©xŽ¶3ÅÑÇVR/bÏ1]X<ö!*ãÜyÜZÅ7ëÌ ¨µ+û°ö(Æ¢äÔ·îg‚ÕÂ\ Db±´D,›»ö˜O#ú«Ž¯!ðÝ ømgR„Ÿ«ø‹³OSØÂÃ)½ +óõ8¾7ë¶î£&FJpŸ¶‚FŒvûMUÆ(øƒ‘ '†+dûq© dâN¦%ÓËÞ–§[§Ä{¹¸øRÒtÊRN—¿¾çoGxùhÅgÄò§=1ŸãúŸFÿKÁ±ÿ¦êô¸ß +r2÷n“ù¯UykGâÞ»è‰ôüî"Lý÷ÞEÊ|·d¨gùÒE]äì­ *§éúŽûÒEfõ+Òº‹úµõvIÞ{ÿ0§ð8¾–xäj_^²ÑÆÿÿÑõ‚ϵø’âúüe–c<`‘?ý€ÿ¯”Í0tãd &Ýöï^ý…ÕÔvVS>7ifñØä™%¡:he‹ú¼ˆf¿=çÿW2)ÈùéISÎOO.ó¿þé_X£ù”¨r¾(>}±ãC—|}úoÏù?÷éÙªþ¬­Žÿú‡Žoûx1ú¢“¥ïB¼èzK¿Ìé;VÇoTÑßþ-•n~Ûòù›ü¬¬Ðêõx×êõ¬P…?k"H†¿?üÛ +ÝßµÐ"ÆF‹dº}’Cw$ÍÐÏøÂ_ýí¹ßVåù®mò'¹]£ÿB±Î÷ÄOVšå ãñ·ç~W•r~×*æ&þëÿYM/ÀêÈ…H&¹Kþ:«ÛpÇØá’ïK¹¾ûhsw7jü;qŒ +˜’Í6o±Õ"4!Kþøý±ß6ùNò'ù9÷/›ò=K÷£ 4!*ûÇïýŽqúÿ¶v-»’ã8vï¯ÈõÕ°Þòv~%D-²ÖóûçAIqãÖt¡1» Öm‰"Eêˆ,w·{8 “ƒ hfÊ1æô¥•)gaNÁcCNA qIÐ)Èbˆ©0ŠàÄHÐéK¿2¹ŠA§rÂt +3ù“ :áÇÅMBRrªGÏ|!ô”ÏmϽ“pÑõúyúñE¯Dªé¼¡ÏøJgCÚ--Îþn¥<€§›µ_)šÞÀÓÏÇ¡Ÿ/XÀÓr+‰ïžâD ¤eí¬ågòTÈbàÐtšõž‚ƒðäÁ(\&÷í +îý:<¹n é_œiÀ/ä)›mÏFž‚¡©Úã¹aþ7”ç<ýxzk%¢Ÿ%°DT4÷ ,§Z]{»š',&f,gš·‚¹fÒ +haƒ4Ñæù?–+‹ìÊ”|\ß9ÒÆ…ú×N3Ískšº{ÑåfZž…'ÍmÍÀA,òõN3Ȫ.…³¦|ã‰ÀATÏx¢r?Þ½Ó"I™œŒ×›MDj< MtÐ ,†ÐDq·ÀD«ñ‘à)_­˜ˆ¯þ¤ +'â§Õ'àDüpI ûi´zÀ‰Øs‚©b™&ÑðŽA_h!u{çß/¹6ݸ(9.×BóøFÛh"JÌ]7ðô¢í3’´Jd’÷Ð›Ö · ‹Ñ5Õ –Ôµn‰‡[kq?°D¢Q6t‚ß7G0HÞé$1kûÛjÿ:/¸³·¿eØû†ñã´ˆ³¡çØüfW<é‘“F0Ø9Þ{oö/òÆ™6”ÈW H…ýè0z ùiL$÷¥œÿO¡“‡—²aYBy¶ƒ‘SÀ‰¢£<ˆŒùƒïq^Ãsœ×Óøÿ ê1œ$¢þND9N-àD ‰™“ÇH™OeÁ‰@óm'âÊÊp"ÒÂö Nõ¯àD$Ÿp¢ƒŒDÆ‚­Û 'Z­œˆŒÜN Éøêµp"~Ûœ'â—K„'Ù„öU4 [˜R¡\dòzÈÿ·SöúŽ¶é¶Ý ™H>]j3ñZá»ÍDRê»e&p(y™PGo­ŸÍõñ?¿o“Bïíëh +‘”úÅv e®j—•xÑsÑX?äQ¶•ÀzBˆU[‰T´•HD„‘H:1¸ŒóÖ¦‘xрⴉëöÃJ¤fƒf"5±§Ê´§Ý/ÁŠ l&âvÛ‰h~Ù‰¤=O›‰‹¯—ú2 ›ÌÃP$- CoLÝ]eh–‡×ê2I9QC³cÚ¡øÓÂÆß’kÑiáB}}b¾Ìãÿæ;Ì,˜ò2\Í–ºN(P}¢†"Ý äê¾L:,ÚOƒ‘\ßé4¤ +}Òš5ï‰Âœó$o^±©H¶ ó’ß=ì¾]@Û |{ËP¤@úö&ˆÛË,\¾Þsž†"ùø“ +H?L–ÂŒe*@—¹MEÒlSàçmS‘];þƉ°‰Nêi)4c—¥@ÇLKá~Úš?å€3ËR ›Ëº€dïï×÷mH’²:‡¡H%àÉa( Çw^†")Wo +Ek1ø"2Iõ2 G;ŒDF(ŒD"=ŒÄ&c.W6 #·ËHDëËH$Ê #w{Ú2©®ã/6IYuÃHà«û²è+ET8Ël$0“DZ¨y—üo'ƒœ:ƒF9ʛŒâÈ„œV¤šr +FÏa.ÒÄë[OÏã$ÁOÒ2š¦ióÆæä2ææì;4ª&µ¼TKMÈãJ@Ÿ“<‚¡bY‹¼×åx§q8™xNÉQ]‡#ÏÎ 9ÔÊüýxOË6Ð3â L3ä`óí¦Ñ×åê’’‹¡º‘×o@ +ÎoùŽs]ÂÎÏ*À}9ä¡9$çƪi˜íÝQP³ +Ò©Ú3?O††v*_}¡úEë[@´;¼7dSð_ù­Þ߅´*t‰Hd3Ù1>Ç"1’Ò¦áo~HÛ_«oÒ\oS”Úej£/9yƒVK‰Ù‚ôH!ät\ÀúYArê¸\™Q¿õ2üÄ“oj«„—YÑG²ZëŠhÚOú7*4k¥!s“µ>º"tádèt¦T“WF‹ÎS}Çm²LæIõ9BcˆÁítߪ© + éé ª¿§ãCÊÝÌcÞ|mUÙt ŒÕQ`8cJ䘟 +=! +Èlõ3|²(uÈɾê}†8Òÿí„€¥xx\Nw¹à„9£|,ãy<Ú«ÔMíu““'3k‡_æå&‡9³w1öz…¥eò±ÜÀ2Ze覜B©2Ñ^G§ª9œgÞïË;™×Épåãì’ôŸOy}xfæotײYÛn¢/:^; +n¾“¼úòWºŠšo_´^a?n½Òû à¤6×Pd½ÖPd%‰Xï uø"G,·c(òˆ•V Å£SqÇ£ Wk(X!°î¡È^CÇPd•VßCR….Ûã%r»Þ̤} Å—§¼‘ë’;f»¡ªæ¦t§„"Õâ¸Âв/È©tÏ?ÿ}BV5ŸnÆvó »°À¹¹ÁtûÄû¢§q?ÿî YG4_ªVœë»Uª{6­Õ£ÉÁ#§›,*´€,ÈÚѯ¬/ãñ?¯§ã¹X•×}ëÇç»ü‘úÕâx«Ll6yÁâ(þ‘´áWuòÖUÓ|q´¿kï¿ÿ ¯Ë±þ?ûýí#ÿúè×J÷m¤ÿƒ€rEd1$ Øox[–¬<‡vÛ¡0ùO%Ë­/ÁŠÖC°¢½¬MÿCÁÊj-ÁÊÏ- ¡k5)ÁÚäÿ¿`×þcdî˼䊡­—¢þc  ‚b¾¤‹Kê‹•x˜î¢P}¶õÏdêË÷ýõÑ¥ÿ@"ÞÆøïZX}–’*潌ÃɽqˆY„IåÑ•‹ä@ §Ò$–ç@ÑàªjFð UGU5õÄôI U€ –úÂ9 œ[B4x’d!æ”\kyÔ8 +˜Š¼ô!,hV—NWšT²´ÊǂãA`)Fõ%¬êž\øŒ±õî[hÑF ~Öèʦ™XyD +vþÅ"ÉluÌÒ,¢»ÏŠ¨®;ÖŽM©¹yx‘;Q쯇uØ°¬¦ÕAꀸÂPœÑaÉÔirÜ¡ã*²(Ú«=`‘ë¾ë…ÅøèjÊc³,tM:Š›Pœ—£ÞÕ¤êÚ¥$0 +?»kŒ§nHCí˜á~`és8¦y5É!e ÃÎØq8{ô4ƒpس¸ßi•zß'ÑBS£ë,+\3`¿ÄÒë"àØPñ[a4âz#bòNŸG’8ǯ +Ò_}Ñâü#:‹6y°ðº‹/³wbD5ÕËÁaØd@˜zL +pñüÐHG³/ÌJÍÎÝNÎZÎMûLöâd¦:@ ,l’Qýî"ƒË³±Îâ©,YÉŠ¯„ÒEÐ}EÓþáœL "ÿ ]r‘ʤRh’ï&Ž"R-vB±g IÌ;fŸ¿)éiMÄB<8ñê¤ô™ÉÅŽ4ï›^ +1—ä +=¾ÞÎäKë4mr]Æ‘]§U<ä¤Éîr­Ä‘-ÍÙb“–v)ÖŠJ@ú‡žÊBÉ!Ÿ`¨7X—ÔÕ{3FâFŠÃ§©î‡?™”iq™‹+ãµ{ˆ™Ä_™e9BO& +e +º1@;ÖIé%«ÙŠ4,FÖ‘‚/6…çP?ì̯oÒy¦ÚzÍ,VgÙLƹxZÆ¢€BV.¬z¹cð@ÿo1("'‡~§oúýãÚ, .xô;Îã‚…à Z©à\Z;ÊbDœ¯»0îœçÑT¨í¸©éø@|ºÓ<äú-G’-N9~ÓCÈ‘ÒÊjû—ããYœßGsUF‡Fò‰vjÏ]gó•–K¡:Kñ$pò›"Ä5'– ¬,_¦é­œÂ™øs²œ-—[4#-¬6ÃøDç̼’œàîkÏwÄÇ© +£vû-ÅøfócL¾âà +ÌÌØjA…:A~"¢’ õN9¬s~,sYpþ `mÓ!¼þÅ`Eh[óÎÓjÊ)àóTyhƒgÓ-ª•Ÿ¬h´ÚrÁÈgQ¶Š¯A£¢ ßxÍÔ«Ì'CiHE;çiÄárËó$ƒ éa¥@$ý­`ôpjõ.ÃÏÜîÕx0ê •"[p,eŠt澂©X$ûÚ“¹ÎÐ —ϹðHðº×OYiÉ»®ªDÊ+ØĈ42¶{M–vßµôÐÑ„cm‚ô/·ß*®(^%÷Å}ç ·§ž—LEèìíÎä ªx +F[‚ó]‚CØLS4¦LÙ”]%sôH!Ë\kŸ¸¥±É°ô½nG; lB‹ñ×JîÒCJ[KÝÁÎqz–üx #Qõíõk»^X¯\¯sJ7JØjÖ…ÝYÓ’å@m¬˜£§´°m×’eýŠ7ZÂú•¬rãÈ.þh^¦úˆ©!¹.z"5†±r ”Àø«~„Î|´Ái{ ô!õÍ@c)èë¡/œkY}'SpÃQïóa¿˜Å0yI?d©{æGóç‘ÝZž–9×ÁjÔãºíï8U%3÷M/©±RøŽã>Z_œ?¿ù2e8Èv"B­Èݲ_[Ö™Úw«*œëK+EËÖä|·ö¶…nLŒt½±Š¿#–#àTù9eƇiÆž>Š÷\îéâÝ°kEoN"=$æñ<$G1¯ÎÇx(õ3¯»‹jv›èS>¤kô4“ <ËA´ÃÒï07Jæ+Ÿ‚FP¾ßû­«Yo°W$/¯PѺªÛºÏ°q(}(ƒ¥=¯)±o5_µèîY¦+GÚ»ãq“â;N˜ÌuÓ;ã͆B±µò7œè¢øŠcö}|Ù¯+Ïi›.õÉaCì÷±¢Gèô°J5†œ)nAsïË[Ð +Dä6ÏNtDnA+\àÐ-É,…n_¢nm¢Ð-h­ºqX…nÁ¹C;Ò‚ÌPCÄ´Š3Fè„#t ºØêʲ/F„pON×3B]‘ê;GqñR<Áa\0¸kdž]ÃÅ}¸>Œ½|\1Bd¹J]Ã&¸–|ÜÀ™ŽE* zú…rÁiÅÛé*¦s)ø% S¬“¶i"R+/œJ´ëÅn×¾L”›ÎŸí.q”Ÿv\Ñ–FA›ãÇ®§–óì¿–ww7¢â#–{ÒŠånŽS@²;GSý¯°m±07¿e_ïô$ÝÏY1UaâÚ‘\Jol IóØqÜÝÇeO%qCDg& sÒÕ35ƒö˜rZjdLð@9øˆärˆ›Bš-¡ä­´¹›wíR –·íÑG׸丣GD!Ÿ9½cç¯ì©V[³¤¥Y†»>º:ðò1V$(Ñ­ž¬Ÿõ#«yÕ@Mk:¶åÞéŠÆƒçÈå„låZzñ§Õ€A —3ZÏ`t|ÖåŒ(ŽK-Qôáô_•.KSLž+èÙD‡$kà -UÓþåXâ#í5Ÿ´Ã¶ø2E5Ó’ü~ººê wÞÔìÒÖ=ã+º/:¥‡#H«Ór`‡qÁ‘ð+Œ z…¤ ºÉ©hq‹D5[©Æ+Œûžhéðit^†è¹—ý~途;ϳügéák­°-8B¾qä +ØsºVØFljÝòɉ°-ƒq!'B»’Åáø¢ÎPÔŒjÁØœüq“ã ±¾À©{u¾á„Ÿ†Ûz;=·?¿é" ÚËZ…*PÊw ws"™~"Ë£¯¥î@®NÊþØq\­õÄI:Ø[¸õ¡æ¼§÷å;î&™%î®^±%£‚«"JÆe–ئ$óÅe×UnŽvp˜w û‰< ›YÃ\½T^ƒˆÍ;Ù@+ ÚzPö™Yš¦U!Êo?vžØ5«êªÛZ*Ò*Éì8î¦W÷dE£ãÑÒOÇSw’Åì“#LWÃœ–; ÆÚuÿfÖÑs$$!ÐŽä2÷‰®ÏʾV  ŠÔ$a➺g‘†ðØ’æIõ6vw‹IÄqu¢Ûsáù'´#ŽKÁ4í@n$XØ\âÿcÓØ’¥ ­<(Iá3>‘2Š›âû’˜FÓgðË +í*”Ëîy?.ÃÂe7«óDuvä¢Î;K‰·t"’K©¨íGDr9C–íÎ"EÂä‚ŽÀmšÊ¯¥v>&¦¢:ÉZV°€Ü="a© +ŽØ.µÇtVHŽÇ¥ +‰YÐVwáŽã~ªÇ_:lé-)¹¶<QϽ¿Í ÍS*þš*g¿Xá©MG$÷óq¿ˆÿ¯e7&ÐjÏg\îäÈ ·nÎu°Âç䙯¿áØéÜ7½3®·/‰ÅÂwwÒúŠí…~~P¡ÙËÛˆ‚,BDA¯$„0ïA‡ÛÑ9CKwÅK®û¢È:º~^ØFFMöY¬Us[»_<¸›Û +!}ödHQì°˜vìÐÐðzQÏÝË°ˆ‚ÓcˇvyÜ.z©úÍ€¥Ívm8ä(€dK›2ŸEÜŒv˹ÖÂHyvãßbqTóxU7ÊýKŸ½ÝŽ©F°±}z +7Ç7]?¶ -Eœ©äºACP/¹îxé²\b­ñÁ™µ^o—ÄìEд4¤8Yyæ%—íÞ¢)04ÉUbŠ«‹\¶UÉÏñ½44ç êcÒN ™ !;â !Æ'B‚Jíñ%òÖ4©Éš5ú(Ä7½yƒ3,,2ìõ #5­³×‡ÈP;SèýDÆû?°5¯‡<¬SN±aÀÊ¡æÚ=Øz*,6õf(î~Žm(êc¾ÜQoZ—T£í¦–/(Åg·KIÕ¦x©mè0ï(oš.õŽ<•º!˜tÒjwg +)Ôr +Tcéºf©KkPyô7Ò>ÁâT#б€Ä Q>D¡¦¶î¨.ÑÀá-/aŽ‡ª”¡\-JùÔ¦D#œdÂf'9ÉóX•xâ䧺Ó«sÛ¾â¥z‚ö>š’ŠŒ2ÝÃÁ^wÔêÈ{\’=;ïGr³üWF!z388ªiõÞ.+y€Ñ’u…ŒjÝU œ`L/|“äκϲŒœ-Õ²ÂL»XËÚÐí`Œi ôíI—¬è,{<[t–u«ywiÝ0‰b‹LßeÞXoï’×$\nØêÀ¹:œ*—à“« ^UÃåÎzlŠ•`ݘúp|õ€è5> + >> + /Pattern << /p5 5 0 R /p6 6 0 R /p7 7 0 R /p8 8 0 R /p9 9 0 R /p10 10 0 R /p11 11 0 R /p12 12 0 R /p13 13 0 R /p14 14 0 R /p15 15 0 R /p16 16 0 R /p17 17 0 R /p18 18 0 R /p19 19 0 R /p20 20 0 R /p21 21 0 R /p22 22 0 R >> +>> +endobj +23 0 obj +<< /Type /Page + /Parent 1 0 R + /MediaBox [ 0 0 400.501068 293.187134 ] + /Contents 3 0 R + /Group << + /Type /Group + /S /Transparency + /I true + /CS /DeviceRGB + >> + /Resources 2 0 R +>> +endobj +5 0 obj +<< /Length 25 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 187.706543 184.616422 ] + /Resources << /XObject << /x24 24 0 R >> >> +>> +stream + /x24 Do + + +endstream +endobj +25 0 obj + 11 +endobj +6 0 obj +<< /Length 27 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 216.053 156.269974 ] + /Resources << /XObject << /x26 26 0 R >> >> +>> +stream + /x26 Do + + +endstream +endobj +27 0 obj + 11 +endobj +7 0 obj +<< /Length 29 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 216.053 99.577062 ] + /Resources << /XObject << /x28 28 0 R >> >> +>> +stream + /x28 Do + + +endstream +endobj +29 0 obj + 11 +endobj +8 0 obj +<< /Length 31 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 216.053 71.230598 ] + /Resources << /XObject << /x30 30 0 R >> >> +>> +stream + /x30 Do + + +endstream +endobj +31 0 obj + 11 +endobj +9 0 obj +<< /Length 33 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 216.053 42.884174 ] + /Resources << /XObject << /x32 32 0 R >> >> +>> +stream + /x32 Do + + +endstream +endobj +33 0 obj + 11 +endobj +10 0 obj +<< /Length 35 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 286.919136 184.61643 ] + /Resources << /XObject << /x34 34 0 R >> >> +>> +stream + /x34 Do + + +endstream +endobj +35 0 obj + 11 +endobj +11 0 obj +<< /Length 37 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 286.919136 156.269974 ] + /Resources << /XObject << /x36 36 0 R >> >> +>> +stream + /x36 Do + + +endstream +endobj +37 0 obj + 11 +endobj +12 0 obj +<< /Length 39 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 286.919136 99.577062 ] + /Resources << /XObject << /x38 38 0 R >> >> +>> +stream + /x38 Do + + +endstream +endobj +39 0 obj + 11 +endobj +13 0 obj +<< /Length 41 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 286.919128 71.230574 ] + /Resources << /XObject << /x40 40 0 R >> >> +>> +stream + /x40 Do + + +endstream +endobj +41 0 obj + 11 +endobj +14 0 obj +<< /Length 43 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 357.78528 184.616422 ] + /Resources << /XObject << /x42 42 0 R >> >> +>> +stream + /x42 Do + + +endstream +endobj +43 0 obj + 11 +endobj +15 0 obj +<< /Length 45 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 456.997872 184.61643 ] + /Resources << /XObject << /x44 44 0 R >> >> +>> +stream + /x44 Do + + +endstream +endobj +45 0 obj + 11 +endobj +16 0 obj +<< /Length 47 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 386.131736 156.269974 ] + /Resources << /XObject << /x46 46 0 R >> >> +>> +stream + /x46 Do + + +endstream +endobj +47 0 obj + 11 +endobj +17 0 obj +<< /Length 49 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 456.997873 156.269976 ] + /Resources << /XObject << /x48 48 0 R >> >> +>> +stream + /x48 Do + + +endstream +endobj +49 0 obj + 11 +endobj +18 0 obj +<< /Length 51 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 386.131736 99.577062 ] + /Resources << /XObject << /x50 50 0 R >> >> +>> +stream + /x50 Do + + +endstream +endobj +51 0 obj + 11 +endobj +19 0 obj +<< /Length 53 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 456.997872 99.577062 ] + /Resources << /XObject << /x52 52 0 R >> >> +>> +stream + /x52 Do + + +endstream +endobj +53 0 obj + 11 +endobj +20 0 obj +<< /Length 55 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 386.131744 71.230654 ] + /Resources << /XObject << /x54 54 0 R >> >> +>> +stream + /x54 Do + + +endstream +endobj +55 0 obj + 11 +endobj +21 0 obj +<< /Length 57 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 401.722288 42.88415 ] + /Resources << /XObject << /x56 56 0 R >> >> +>> +stream + /x56 Do + + +endstream +endobj +57 0 obj + 11 +endobj +22 0 obj +<< /Length 59 0 R + /PatternType 1 + /BBox [ 0 0 403 1781 ] + /XStep 403 + /YStep 1781 + /TilingType 1 + /PaintType 1 + /Matrix [ 0.008 0 -0 0.008 456.997872 42.884118 ] + /Resources << /XObject << /x58 58 0 R >> >> +>> +stream + /x58 Do + + +endstream +endobj +59 0 obj + 11 +endobj +24 0 obj +<< /Length 61 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 60 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +61 0 obj + 100 +endobj +60 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +26 0 obj +<< /Length 63 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 62 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +63 0 obj + 100 +endobj +62 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +28 0 obj +<< /Length 65 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 64 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +65 0 obj + 100 +endobj +64 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +30 0 obj +<< /Length 67 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 66 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +67 0 obj + 100 +endobj +66 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +32 0 obj +<< /Length 69 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 68 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +69 0 obj + 100 +endobj +68 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +34 0 obj +<< /Length 71 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 70 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +71 0 obj + 100 +endobj +70 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +36 0 obj +<< /Length 73 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 72 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +73 0 obj + 100 +endobj +72 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +38 0 obj +<< /Length 75 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 74 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +75 0 obj + 100 +endobj +74 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +40 0 obj +<< /Length 77 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 76 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +77 0 obj + 100 +endobj +76 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +42 0 obj +<< /Length 79 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 78 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +79 0 obj + 100 +endobj +78 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +44 0 obj +<< /Length 81 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 80 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +81 0 obj + 100 +endobj +80 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +46 0 obj +<< /Length 83 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 82 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +83 0 obj + 100 +endobj +82 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +48 0 obj +<< /Length 85 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 84 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +85 0 obj + 100 +endobj +84 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +50 0 obj +<< /Length 87 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 86 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +87 0 obj + 100 +endobj +86 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +52 0 obj +<< /Length 89 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 88 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +89 0 obj + 100 +endobj +88 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +54 0 obj +<< /Length 91 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 90 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +91 0 obj + 100 +endobj +90 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +56 0 obj +<< /Length 93 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 92 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +93 0 obj + 100 +endobj +92 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +58 0 obj +<< /Length 95 0 R + /Filter /FlateDecode + /Type /XObject + /Subtype /Form + /BBox [ 0 0 403 1781 ] + /Resources 94 0 R +>> +stream +xœŠ1 +€0û{Å~À󢓼@,¢¥Xˆ‚ Z ¿o”eØÙD‚/C‹rì7YÁ“Ç.÷ i†°`#Eó‹Æyƒõ¢JÙšÊA*\¨g¯šµkØÚ€#"Ezk, +endstream +endobj +95 0 obj + 100 +endobj +94 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +1 0 obj +<< /Type /Pages + /Kids [ 23 0 R ] + /Count 1 +>> +endobj +96 0 obj +<< /Creator (cairo 1.14.1 (http://cairographics.org)) + /Producer (cairo 1.14.1 (http://cairographics.org)) +>> +endobj +97 0 obj +<< /Type /Catalog + /Pages 1 0 R +>> +endobj +xref +0 98 +0000000000 65535 f +0000047762 00000 n +0000035514 00000 n +0000000015 00000 n +0000035490 00000 n +0000036039 00000 n +0000036333 00000 n +0000036624 00000 n +0000036914 00000 n +0000037204 00000 n +0000037494 00000 n +0000037788 00000 n +0000038083 00000 n +0000038377 00000 n +0000038671 00000 n +0000038965 00000 n +0000039259 00000 n +0000039554 00000 n +0000039849 00000 n +0000040143 00000 n +0000040437 00000 n +0000040731 00000 n +0000041024 00000 n +0000035810 00000 n +0000041318 00000 n +0000036311 00000 n +0000041676 00000 n +0000036602 00000 n +0000042034 00000 n +0000036892 00000 n +0000042392 00000 n +0000037182 00000 n +0000042750 00000 n +0000037472 00000 n +0000043108 00000 n +0000037766 00000 n +0000043466 00000 n +0000038061 00000 n +0000043824 00000 n +0000038355 00000 n +0000044182 00000 n +0000038649 00000 n +0000044540 00000 n +0000038943 00000 n +0000044898 00000 n +0000039237 00000 n +0000045256 00000 n +0000039532 00000 n +0000045614 00000 n +0000039827 00000 n +0000045972 00000 n +0000040121 00000 n +0000046330 00000 n +0000040415 00000 n +0000046688 00000 n +0000040709 00000 n +0000047046 00000 n +0000041002 00000 n +0000047404 00000 n +0000041296 00000 n +0000041603 00000 n +0000041580 00000 n +0000041961 00000 n +0000041938 00000 n +0000042319 00000 n +0000042296 00000 n +0000042677 00000 n +0000042654 00000 n +0000043035 00000 n +0000043012 00000 n +0000043393 00000 n +0000043370 00000 n +0000043751 00000 n +0000043728 00000 n +0000044109 00000 n +0000044086 00000 n +0000044467 00000 n +0000044444 00000 n +0000044825 00000 n +0000044802 00000 n +0000045183 00000 n +0000045160 00000 n +0000045541 00000 n +0000045518 00000 n +0000045899 00000 n +0000045876 00000 n +0000046257 00000 n +0000046234 00000 n +0000046615 00000 n +0000046592 00000 n +0000046973 00000 n +0000046950 00000 n +0000047331 00000 n +0000047308 00000 n +0000047689 00000 n +0000047666 00000 n +0000047828 00000 n +0000047956 00000 n +trailer +<< /Size 98 + /Root 97 0 R + /Info 96 0 R +>> +startxref +48009 +%%EOF Index: lxp32/tags/1.0/doc/src/trm/images/blockdiagram.svg =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/blockdiagram.svg (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/blockdiagram.svg (revision 5) @@ -0,0 +1,782 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + Fetch + + Decode + + Execute + + Scratchpad + + + + + read + write + + Interruptmultiplexer + + + + Instructioncache(LXP32C) + + + + + irq_i + clk_i + rst_i + + + Instruction busLow LatencyInterface(LXP32U) + Instruction busWISHBONE(LXP32C) + + Data busWISHBONE + + + enter + return + + + + jump + + + + + Index: lxp32/tags/1.0/doc/src/trm/images/lxp32-logo.pdf =================================================================== --- lxp32/tags/1.0/doc/src/trm/images/lxp32-logo.pdf (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/images/lxp32-logo.pdf (revision 5) @@ -0,0 +1,68 @@ +%PDF-1.5 +%µí®û +3 0 obj +<< /Length 4 0 R + /Filter /FlateDecode +>> +stream +xœmNIÃ0¼óŠù@S ö7ú„÷à’þ_ªq–Ji…€0Ì,ÄðX3î##¿IŒÄ ¢¸yY'ÌÄX2à.‘h†:êZÏò‚hj¨4äY¶g_Ô–{ÒÉœ›°F½÷þÙ9Lûú—ô äµ,A +endstream +endobj +4 0 obj + 116 +endobj +2 0 obj +<< + /ExtGState << + /a0 << /CA 1 /ca 1 >> + >> +>> +endobj +5 0 obj +<< /Type /Page + /Parent 1 0 R + /MediaBox [ 0 0 432 432 ] + /Contents 3 0 R + /Group << + /Type /Group + /S /Transparency + /I true + /CS /DeviceRGB + >> + /Resources 2 0 R +>> +endobj +1 0 obj +<< /Type /Pages + /Kids [ 5 0 R ] + /Count 1 +>> +endobj +6 0 obj +<< /Creator (cairo 1.14.1 (http://cairographics.org)) + /Producer (cairo 1.14.1 (http://cairographics.org)) +>> +endobj +7 0 obj +<< /Type /Catalog + /Pages 1 0 R +>> +endobj +xref +0 8 +0000000000 65535 f +0000000516 00000 n +0000000230 00000 n +0000000015 00000 n +0000000208 00000 n +0000000302 00000 n +0000000581 00000 n +0000000708 00000 n +trailer +<< /Size 8 + /Root 7 0 R + /Info 6 0 R +>> +startxref +760 +%%EOF Index: lxp32/tags/1.0/doc/src/trm/.gitignore =================================================================== --- lxp32/tags/1.0/doc/src/trm/.gitignore (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/.gitignore (revision 5) @@ -0,0 +1,6 @@ +/*.aux +/*.log +/*.gz +/*.toc +/*.pdf +/*.out Index: lxp32/tags/1.0/doc/src/trm/preamble.tex =================================================================== --- lxp32/tags/1.0/doc/src/trm/preamble.tex (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/preamble.tex (revision 5) @@ -0,0 +1,88 @@ +\usepackage{microtype} +\usepackage{alltt} +\usepackage{amsmath} +\usepackage[charter]{mathdesign} + +\usepackage{fontspec} +\setmainfont[Ligatures=TeX]{XCharter} +\setmonofont[Scale=MatchLowercase]{DejaVu Sans Mono} + +\usepackage[english]{babel} + +\usepackage[perpage]{footmisc} + +\newcommand{\styledtitleref}[1]{\emph{\titleref{#1}}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Various Memoir class settings +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\sloppybottom +\setsecnumdepth{section} +\settocdepth{section} + +\setpnumwidth{2.55em} +\setrmarg{3.55em} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Code +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcommand{\shellcmd}[1]{\texttt{#1}} +\newcommand{\code}[1]{\mbox{\texttt{#1}}} +\newcommand{\instr}[1]{\texttt{\textbf{#1}}} +\newcommand{\instrname}[1]{\emph{#1}} +\newcommand{\signal}[1]{\texttt{#1}} + +\newenvironment{codepar}{% + \vspace{0.5\baselineskip}% + \begin{minipage}{0.9\textwidth}\begin{alltt} +}{% + \end{alltt}\end{minipage}% + \vspace{0.5\baselineskip} +} + +\newenvironment{codeparbreakable}{% + \vspace{0.5\baselineskip}% + \begin{alltt} + }{% + \end{alltt}% + \vspace{0.5\baselineskip} +} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Tables +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcolumntype{L}{>{\raggedright\arraybackslash}X} +\newcolumntype{R}{>{\raggedleft\arraybackslash}X} +\newcolumntype{C}{>{\centering\arraybackslash}X} + +\newcolumntype{Q}[1]{>{\raggedright\arraybackslash}m{#1}} +\newcolumntype{E}[1]{>{\raggedleft\arraybackslash}m{#1}} +\newcolumntype{W}[1]{>{\centering\arraybackslash}m{#1}} + +\newcommand{\tabcutin}[2]{\multicolumn{#1}{c}{\emph{#2}}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Various stuff +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\newcommand{\cplusplus}{C\texttt{\raisebox{0.05em}{++}}} +\newcommand{\lxp}{\textls[60]{LXP}32} +\newcommand{\tocitem}[2]{\phantomsection\addcontentsline{toc}{#1}{#2}} + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Hyperlinks +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +\usepackage[bookmarks=true, + bookmarksnumbered=true, + bookmarksdepth=2, + hypertexnames=false] + {hyperref} + +\hypersetup{ + pdftitle={LXP32 Technical Reference Manual}, + pdfauthor={Alex I. Kuznetsov} +} Index: lxp32/tags/1.0/doc/src/trm/lxp32-trm.tex =================================================================== --- lxp32/tags/1.0/doc/src/trm/lxp32-trm.tex (nonexistent) +++ lxp32/tags/1.0/doc/src/trm/lxp32-trm.tex (revision 5) @@ -0,0 +1,1677 @@ +% !TEX TS-program = lualatex +\documentclass[a4paper,12pt,twoside,extrafontsizes]{memoir} + +\input{preamble.tex} + +\begin{document} + +\input{frontmatter.tex} + +\mainmatter + +\chapter{Introduction} + +\section{Main features} + +\lxp{} (\emph{Lightweight eXecution Pipeline}) is a small 32-bit CPU IP core optimized for FPGA implementation. Its key features include: + +\begin{itemize} + \item described in portable VHDL-93, not tied to any particular vendor; + \item 3-stage pipeline; + \item 256 registers implemented as a RAM block; + \item simple instruction set with less than 30 distinct opcodes; + \item separate instruction and data buses, optional instruction cache; + \item WISHBONE compatible; + \item 8 interrupts with hardwired priorities; + \item optional divider. +\end{itemize} + +Being a lightweight IP core, \lxp{} also has certain limitations: + +\begin{itemize} + \item no branch prediction; + \item no floating-point unit; + \item no memory management unit; + \item no nested interrupt handling; + \item no debugging facilities. +\end{itemize} + +Two major hardware versions of the CPU are provided: \lxp{}U which does not include an instruction cache and uses the Low Latency Interface (Section \ref{sec:lli}) to fetch instructions, and \lxp{}C which fetches instructions over a cached WISHBONE bus protocol. These versions are otherwise identical and have the same instruction set architecture. + +\section{Implementation estimates} + +Typical results of \lxp{} core FPGA implementation are presented in Table \ref{tab:implementation}. Note that these data are only useful as rough estimates, since actual results depend greatly on tool versions and configuration, design constraints, device utilization ratio and other factors. + +Data on two configurations are provided: + +\begin{itemize} + \item \emph{Compact}: \lxp{}U (without instruction cache), no divider, 2-cycle multiplier. + \item \emph{Full}: \lxp{}C (with instruction cache), divider, 2-cycle multiplier. +\end{itemize} + +The slowest speed grade was used for clock frequency estimation. + +\begin{table}[htbp] + \caption{Typical results of \lxp{} core FPGA implementation} + \label{tab:implementation} + \begin{tabularx}{\textwidth}{Q{0.5\textwidth}LL} + \toprule + Resource & Compact & Full \\ + \midrule + \multicolumn{3}{c}{Altera\textregistered{} Cyclone\textregistered{} V 5CEBA2F23C8} \\ + \midrule + Logic Array Blocks (LABs) & 79 & 119 \\ + \hspace*{1em}ALMs & 630 & 972 \\ + \hspace*{2em}ALUTs & 982 & 1531 \\ + \hspace*{2em}Flip-flops & 537 & 942 \\ + DSP blocks & 3 & 3 \\ + RAM blocks (M10K) & 2 & 3 \\ + Clock frequency & 103.9 MHz & 98.8 MHz \\ + \midrule + \multicolumn{3}{c}{Microsemi\textregistered{} IGLOO\textregistered{}2 M2GL005-FG484} \\ + \midrule + Logic elements (LUT+DFF) & 1529 & 2226 \\ + \hspace*{1em}LUTs & 1471 & 2157 \\ + \hspace*{1em}Flip-flops & 718 & 1181 \\ + Mathblocks (MACC) & 3 & 3 \\ + RAM blocks (RAM1K18) & 2 & 3 \\ + Clock frequency & 111.7 MHz & 107.8 MHz \\ + \midrule + \multicolumn{3}{c}{Xilinx\textregistered{} Artix\textregistered{}-7 xc7a15tfgg484-1} \\ + \midrule + Slices & 264 & 381 \\ + \hspace*{1em}LUTs & 809 & 1151 \\ + \hspace*{1em}Flip-flops & 527 & 923 \\ + DSP blocks (DSP48E1) & 4 & 4 \\ + RAM blocks (RAMB18E1) & 2 & 3 \\ + Clock frequency & 113.6 MHz & 109.3 MHz \\ + \bottomrule + \end{tabularx} +\end{table} + +\section{Structure of this manual} + +General description of the \lxp{} operation from a software developer's point of view can be found in Chapter \ref{ch:isa}, \styledtitleref{ch:isa}. Future versions of the \lxp{} CPU are intended to be at least backwards compatible with this architecture. + +Topics related to hardware, such as synthesis, implementation and interfacing other IP cores, are covered in Chapter \ref{ch:integration}, \styledtitleref{ch:integration}. The \lxp{} IP core package also includes testbenches which can be used to simulate the design as described in Chapter \ref{ch:simulation}, \styledtitleref{ch:simulation}. + +Tools shipped as parts of the \lxp{} IP core package (assembler/linker, disassembler and interconnect generator) are documented in Chapter \ref{ch:developmenttools}, \styledtitleref{ch:developmenttools}. + +Appendices include a detailed description of the \lxp{} instruction set, instruction cycle counts and \lxp{} assembly language definition. WISHBONE datasheet required by the WISHBONE specification is also provided. + +\chapter{Instruction set architecture} +\label{ch:isa} + +\section{Data format} + +Most \lxp{} instructions work with 32-bit data words. A few instructions that address individual bytes use little-endian order, that is, the least significant byte is stored at the lowest address. Signed values are encoded in a 2's complement format. + +\section{Instruction format} +\label{sec:instructionformat} + +All \lxp{} instructions are encoded as 32-bit words, with the exception of \instr{lc} (\instrname{Load Constant}), which occupies two adjacent 32-bit words. Instructions in memory must be aligned to word boundaries. + +Most arithmetic and logical instructions take two source operands and write the result to an independent destination register. General instruction format is presented on Figure \ref{fig:instructionformat}. + +\begin{figure}[htbp] + \centering + \includegraphics[scale=1.2]{images/instructionformat.pdf} + \caption{\lxp{} instruction format} + \label{fig:instructionformat} +\end{figure} + +This format includes the following fields: + +\begin{enumerate} + \item OPCODE -- a 6-bit instruction code (see Appendix \ref{app:instructionset}). + \item T1 -- type of the RD1 field. + \item T2 -- type of the RD2 field. + \item DST -- register number (usually the destination register). + \item RD1 -- register/direct operand 1. + \item RD2 -- register/direct operand 2. +\end{enumerate} + +Some of these fields may not have meaning for a particular instruction; such unused fields are replaced with zeros. + +DST field specifies one of the 256 \lxp{} registers. RD1 and RD2 fields can denote either source register operands or direct (immediate) operands: if the corresponding T field is 1, RD value is a register number, otherwise it is interpreted as a direct signed byte in a 2's complement format (valid values range from -128 to 127). + +For example, consider the following instruction that adds \code{10} to \code{r0} and writes the result to \code{r1}: + +\begin{codepar} + \instr{add} r1, r0, 10 +\end{codepar} + +In this example, OPCODE is \code{010000}, T1 is \code{1}, T2 is \code{0}, DST is \code{00000001}, RD1 is \code{00000000} and RD2 is \code{00001010}. Hence, the instruction is encoded as \code{0x4201000A}. + +For convenience, some instructions have alias mnemonics. For example, \lxp{} does not have a distinct \instr{mov} opcode: instead, \code{\instr{mov} dst, src} is an alias for \code{\instr{add} dst, src, 0}. + +A complete list of \lxp{} instructions is provided in Appendix \ref{app:instructionset}. + +\section{Registers} + +\lxp{} has 256 registers denoted as \code{r0} -- \code{r255}. The first 240 of them (from \code{r0} to \code{r239}) are general-purpose registers (GPR), the last 16 (from \code{r240} to \code{r255}) are special-purpose registers (SPR). For convenience, some special-purpose registers have alias names: for example, \code{r255} can be also referred to as \code{sp} (stack pointer). Special purpose registers are listed in Table \ref{tab:spr}. Some of these registers are reserved: the software should not access them. + +\begin{table}[htbp] + \caption{\lxp{} special-purpose registers} + \label{tab:spr} + \begin{tabularx}{\textwidth}{llL} + \toprule + Alias name & Generic name & Description \\ + \midrule + \code{iv0} & \code{r240} & Interrupt vector 0 (Section \ref{sec:interrupthandling}) \\ + \code{iv1} & \code{r241} & Interrupt vector 1 (Section \ref{sec:interrupthandling}) \\ + \code{iv2} & \code{r242} & Interrupt vector 2 (Section \ref{sec:interrupthandling}) \\ + \code{iv3} & \code{r243} & Interrupt vector 3 (Section \ref{sec:interrupthandling}) \\ + \code{iv4} & \code{r244} & Interrupt vector 4 (Section \ref{sec:interrupthandling}) \\ + \code{iv5} & \code{r245} & Interrupt vector 5 (Section \ref{sec:interrupthandling}) \\ + \code{iv6} & \code{r246} & Interrupt vector 6 (Section \ref{sec:interrupthandling}) \\ + \code{iv7} & \code{r247} & Interrupt vector 7 (Section \ref{sec:interrupthandling}) \\ + \multicolumn{1}{l}{---} & \code{r248}\,--\,\code{r251} & \emph{Reserved} \\ + \code{cr} & \code{r252} & Control register (Section \ref{sec:interrupthandling}) \\ + \code{irp} & \code{r253} & Interrupt return pointer (Section \ref{sec:interrupthandling}) \\ + \code{rp} & \code{r254} & Return pointer (Section \ref{sec:callingprocedures})\\ + \code{sp} & \code{r255} & Stack pointer (Section \ref{sec:stack}) \\ + \bottomrule + \end{tabularx} +\end{table} + +All registers are zero-initialized during the CPU reset. + +\section{Addressing} +\label{sec:addressing} + +All addressing in \lxp{} is indirect. In order to access a memory location, its address must be stored in a register; any available register can be used for this purpose. + +Some instructions, namely \instr{lsb} (\instrname{Load Signed Byte}), \instr{lub} (\instrname{Load Unsigned Byte}) and \instr{sb} (\instrname{Store Byte}) provide byte-granular access, in which case all 32 bits in the address are significant. Otherwise the least two address bits are ignored as \lxp{} doesn't support unaligned access to 32-bit data words (during simulation, a warning is emitted if such a transaction is attempted). + +A special rule applies to pointers that refer to instructions: since instructions are always word-aligned, the least significant bit is interpreted as the \code{IRF} (\emph{Interrupt Return Flag}). See Section \ref{sec:interrupthandling} for details. + +\section{Stack} +\label{sec:stack} + +The current pointer to the top of the stack is stored in the \code{sp} register. To the hardware this register is not different from general purpose registers, that is, in no situation does the CPU access the stack implicitly (procedure calls and interrupts use register-based conventions). + +Software can access the stack as follows: + +\begin{codepar} + \emph{// push r0 on the stack} + \instr{sub} sp, sp, 4 + \instr{sw} sp, r0 + \emph{// pop r0 from the stack} + \instr{lw} r0, sp + \instr{add} sp, sp, 4 +\end{codepar} + +Before using the stack, the \code{sp} register must be set up to point to a valid memory location. The simplest software can operate stackless, or even without data memory altogether if registers are enough to store the program state. + +\section{Calling procedures} +\label{sec:callingprocedures} + +\lxp{} provides a \instr{call} instruction which stores the address of the next instruction in the \code{rp} register and transfers execution to the procedure pointed by \instr{call} operand. Return from a procedure is performed by \code{\instr{jmp} rp} instruction which also has \instr{ret} alias. + +If a procedure must in turn call some procedure itself, the return pointer in the \code{rp} register will be overwritten by the \instr{call} instruction. Hence the procedure must save its value somewhere; the most general solution is to use the stack: + +\begin{codepar} + \instr{sub} sp, sp, 4 + \instr{sw} sp, rp + ... + \instr{call} r1 + ... + \instr{lw} rp, sp + \instr{add} sp, sp, 4 + \instr{ret} +\end{codepar} + +Procedures that don't use the \instr{call} instruction (sometimes called \emph{leaf} procedures) don't need to save the \code{rp} value. + +Since \instr{ret} is just an alias for \code{\instr{jmp} rp}, one can also use \instrname{Compare and Jump} instructions (\instr{cjmp\emph{xxx}}) to perform a conditional procedure return. + +Although the \lxp{} architecture doesn't mandate any particular calling convention, some general recommendations are presented below: + +\begin{enumerate} + \item Pass arguments through the \code{r1}--\code{r31} registers. + \item Return value through the \code{r0} register. + \item Designate \code{r0}--\code{r31} registers as \emph{caller-saved}, that is, they are not guaranteed to be preserved during procedure calls and must be saved by the caller if needed. The procedure can use them for any purpose, regardless of whether they are used to pass arguments and/or return values. For obvious reasons, this rule does not apply to interrupt handlers. +\end{enumerate} + +\section{Interrupt handling} +\label{sec:interrupthandling} + +\subsection{Control register} + +\lxp{} supports 8 interrupts with hardwired priority levels (interrupts with lower vector numbers have higher priority). Interrupts vectors (pointers to interrupt handlers) are stored in the \code{iv0}--\code{iv7} registers. Interrupt handling is controlled by the \code{cr} register (Table \ref{tab:cr}). + +\begin{table}[htbp] + \caption{Control register} + \label{tab:cr} + \begin{tabularx}{\textwidth}{lL} + \toprule + Bit & Description \\ + \midrule + 0 & Enable interrupt 0 \\ + 1 & Enable interrupt 1 \\ + & \ldots \\ + 7 & Enable interrupt 7 \\ + 8 & Temporarily block interrupt 0 \\ + 9 & Temporarily block interrupt 1 \\ + & \ldots \\ + 15 & Temporarily block interrupt 7 \\ + 31--16 & \emph{Reserved} \\ + \bottomrule + \end{tabularx} +\end{table} + +Disabled interrupts are ignored altogether: if the CPU receives an interrupt request signal while the corresponding interrupt is disabled, the interrupt handler will not be called even if the interrupt is enabled later. Conversely, temporarily blocked interrupts are still registered, but their handlers are not called until they are unblocked. + +Like other registers, \code{cr} is zero-initialized during the CPU reset, meaning that no interrupts are initially enabled. + +\subsection{Invoking interrupt handlers} + +Interrupt handlers are invoked by the CPU similarly to procedures (Section \ref{sec:callingprocedures}), the difference being that in this case return address is stored in the \code{irp} register (as opposed to \code{rp}), and the least significant bit of the register (\code{IRF} -- \emph{Interrupt Return Flag}) is set. + +An interrupt handler returns using the \code{\instr{jmp} irp} instruction which also has \instr{iret} alias. Until the interrupt handler returns, the CPU will defer further interrupt processing (although incoming interrupt requests will still be registered). This also means that \code{irp} register value will not be unexpectedly overwritten. When executing the \code{\instr{jmp} irp} instruction, the CPU will recognize the \code{IRF} flag and resume interrupt processing as usual. This behavior can be exploited to perform a conditional return from the interrupt handler, similarly to the technique described in Section \ref{sec:callingprocedures} for conditional procedure returns. + +Another technique can be useful when waiting for a single event, such as a coprocessor finishing its job: the interrupt handler can be set up to return to a designated address instead of the address stored in the \code{irp} register. This designated address must have the \code{IRF} flag set, otherwise all further interrupt processing will be disabled: + +\begin{codepar} + \instr{lc} r0, continue@1 \emph{// IRF flag} + \instr{lc} iv0, handler + ... \emph{// issue coprocessor command} + \instr{hlt} \emph{// wait for an interrupt} +continue: + ... \emph{// the execution will continue here} +handler: + \instr{jmp} r0 +\end{codepar} + +\chapter{Integration} +\label{ch:integration} + +\section{Overview} + +The \lxp{} IP core is delivered in a form of a synthesizable RTL description expressed in \mbox{VHDL-93}. It does not use any technology specific primitives and should work out of the box with major FPGA synthesis software. \lxp{} can be integrated in both VHDL and Verilog\textregistered{} based SoC designs. + +Major \lxp{} hardware versions have separate top-level design units: + +\begin{itemize} + \item \shellcmd{lxp32u\_top} -- \lxp{}U (without instruction cache), + \item \shellcmd{lxp32c\_top} -- \lxp{}C (with instruction cache). +\end{itemize} + +A high level block diagram of the CPU is presented on Figure \ref{fig:blockdiagram}. Schematic symbols for \lxp{}U and \lxp{}C are shown on Figure \ref{fig:symbols}. + +\begin{figure}[htbp] + \centering + \includegraphics[scale=0.85]{images/blockdiagram.pdf} + \caption{\lxp{} CPU block diagram} + \label{fig:blockdiagram} +\end{figure} + +\begin{figure}[htbp] + \centering + \includegraphics[scale=0.85]{images/symbols.pdf} + \caption{Schematic symbols for \lxp{}U and \lxp{}C} + \label{fig:symbols} +\end{figure} + +\lxp{}U uses the Low Latency Interface (Section \ref{sec:lli}) to fetch instructions. This interface is designed to interact with low latency on-chip peripherals such as RAM blocks or similar devices that are generally expected to return data word after one cycle since the instruction address has been set. It can be also connected to a custom (external) instruction cache. + +To achieve the least possible latency, some LLI signals are not registered. For this reason the LLI is not suitable for interaction with off-chip peripherals. + +\lxp{}C fetches instructions over the WISHBONE instruction bus. To maximize throughput, it supports the WISHBONE registered feedback signals [CTI\_O()] and [BTE\_O()]. All outputs on this bus are registered. This version is recommended for use with high latency memory devices such as SDRAM chips, as well as for situations where LLI combinatorial delays are unacceptable. + +Both \lxp{}U and \lxp{}C use WISHBONE protocol for the data bus. + +\section{Ports} + +\begin{ctabular}{lccl} + \toprule + Port & Direction & Bus width & Description \\ + \midrule + \tabcutin{4}{Global signals} \\ + \midrule + \signal{clk\_i} & in & 1 & System clock \\ + \signal{rst\_i} & in & 1 & Synchronous reset, active high \\ + \midrule + \tabcutin{4}{Instruction bus -- Low Latency Interface (\lxp{}U only)} \\ + \midrule + \signal{lli\_re\_o} & out & 1 & Read enable output, active high \\ + \signal{lli\_adr\_o} & out & 30 & Address output \\ + \signal{lli\_dat\_i} & in & 32 & Data input \\ + \signal{lli\_busy\_i} & in & 1 & Busy flag input, active high \\ + \midrule + \tabcutin{4}{Instruction bus -- WISHBONE (\lxp{}C only)} \\ + \midrule + \signal{ibus\_cyc\_o} & out & 1 & Cycle output \\ + \signal{ibus\_stb\_o} & out & 1 & Strobe output \\ + \signal{ibus\_cti\_o} & out & 3 & Cycle type identifier \\ + \signal{ibus\_bte\_o} & out & 2 & Burst type extension \\ + \signal{ibus\_ack\_i} & in & 1 & Acknowledge input \\ + \signal{ibus\_adr\_o} & out & 30 & Address output \\ + \signal{ibus\_dat\_i} & in & 32 & Data input \\ + \midrule + \tabcutin{4}{Data bus} \\ + \midrule + \signal{dbus\_cyc\_o} & out & 1 & Cycle output \\ + \signal{dbus\_stb\_o} & out & 1 & Strobe output \\ + \signal{dbus\_we\_o} & out & 1 & Write enable output \\ + \signal{dbus\_sel\_o} & out & 4 & Select output \\ + \signal{dbus\_ack\_i} & in & 1 & Acknowledge input \\ + \signal{dbus\_adr\_o} & out & 30 & Address output \\ + \signal{dbus\_dat\_o} & out & 32 & Data output \\ + \signal{dbus\_dat\_i} & in & 32 & Data input \\ + \midrule + \tabcutin{4}{Other ports} \\ + \midrule + \signal{irq\_i} & in & 8 & Interrupt requests \\ + \bottomrule +\end{ctabular} + +\section{Generics} +\label{sec:generics} + +The following generics can be used to configure the \lxp{} IP core parameters. + +\subsection{DBUS\_RMW} + +By default, \lxp{} uses the \signal{dbus\_sel\_o} (byte enable) port to perform byte-granular write transactions initiated by the \instr{sb} (\instrname{Store Byte}) instruction. If this option is set to \code{true}, \signal{dbus\_sel\_o} is always tied to \code{"1111"}, and byte-granular write access is performed using the RMW (read-modify-write) cycle. The latter method is slower, but can work with slaves that do not have the [SEL\_I()] port. + +This feature requires data bus transactions to be idempotent, that is, repeating a transaction must not alter the slave state. Care should be taken with non-memory slaves to ensure that this condition is satisfied. + +\subsection{DIVIDER\_EN} + +\lxp{} includes a divider unit which occupies a considerable amount of resources. It can be excluded by setting this option to \code{false}. + +\subsection{IBUS\_BURST\_SIZE} + +Instruction bus burst size. Default value is 16. Only for \lxp{}C. + +\subsection{IBUS\_PREFETCH\_SIZE} + +Number of words that the instruction cache will read ahead from the current instruction pointer. Default value is 32. Only for \lxp{}C. + +\subsection{MUL\_ARCH} + +\lxp{} provides three multiplier options: + +\begin{itemize} + \item \code{"dsp"} is the fastest architecture designed for technologies that provide fast parallel $16 \times 16$ multipliers, which includes most modern FPGA families. One multiplication takes 2 clock cycles. + \item \code{"opt"} architecture uses a semi-parallel multiplication algorithm based on carry-save accumulation of partial products. It is designed for technologies that do not provide fast $16 \times 16$ multipliers. One multiplication takes 6 clock cycles. + \item \code{"seq"} is a fully sequential design. One multiplication takes 34 clock cycles. +\end{itemize} + +The default multiplier architecture is \code{"dsp"}. This option is recommended for most modern FPGA devices regardless of optimization goal since it is not only the fastest, but also occupies the least amount of general-purpose logic resources. However, it will create a timing bottleneck on technologies that lack fast multipliers. + +For older FPGA families that don't provide dedicated multipliers the \code{"opt"} architecture can be used if decent throughput is still needed. It is designed to avoid creating a timing bottleneck on such technologies. Alternatively, \code{"seq"} architecture can be used when throughput is not a concern. + +\subsection{START\_ADDR} + +Address of the first instruction to be executed after CPU reset. Default value is \code{0}. Note that it is a 30-bit value as it is used to address 32-bit words, not bytes. + +\section{Clock and reset} +\label{sec:clockreset} + +All flip-flops in the CPU are triggered by a rising edge of the \signal{clk\_i} signal. No specific requirements are imposed on the \signal{clk\_i} signal apart from usual constraints on setup and hold times. + +\lxp{} is reset synchronously when the \signal{rst\_i} signal is asserted. If the system reset signal comes from an asynchronous source, a synchronization circuit must be used; an example of such a circuit is shown on Figure \ref{fig:resetsync}. + +\begin{figure}[htbp] + \centering + \includegraphics[scale=1]{images/resetsync.pdf} + \caption{Reset synchronization circuit} + \label{fig:resetsync} +\end{figure} + +In SRAM-based FPGAs flip-flops and RAM blocks have deterministic state after a bitstream is loaded. On such technologies \lxp{} can operate without reset. In this case the \signal{rst\_i} port can be tied to a logical \code{0} in the RTL design to allow the synthesizer to remove redundant logic. + +\signal{clk\_i} and \signal{rst\_i} signals also serve the role of [CLK\_I] and [RST\_I] WISHBONE signals, respectively, for both instruction and data buses. + +\section{Low Latency Interface} +\label{sec:lli} + +Low Latency Interface is a simple pipelined synchronous protocol with a typical latency of 1 cycle used by \lxp{}U to fetch instructions. Its timing diagram is shown on Figure \ref{fig:llitiming}. The request is considered valid when \signal{lli\_re\_o} is high and \signal{lli\_busy\_i} is low on the same clock cycle. On the next cycle after the request is valid the slave must either produce data on \signal{lmi\_dat\_i} or assert \signal{lli\_busy\_i} to indicate that data are not ready. Note that the values of \signal{lli\_re\_o} and \signal{lli\_adr\_o} are not guaranteed to be preserved by the CPU while the slave is busy. + +The simplest, ``always ready'' slaves such as on-chip RAM blocks can be trivially connected to the LLI by connecting address, data and read enable ports and tying the \signal{lli\_busy\_i} signal to a logical \code{0}. Slaves are also allowed to introduce wait states, which makes it possible to implement external caching. + +\begin{figure}[htbp] + \centering + \includegraphics[scale=1]{images/llitiming.pdf} + \caption{Low Latency Interface timing diagram (\lxp{}U)} + \label{fig:llitiming} +\end{figure} + +Note that the \signal{lli\_adr\_o} signal has a width of 30 bits since it addresses words, not bytes (instructions are always word-aligned). + +Since this interface is not registered, it is not suitable for interaction with off-chip peripherals. Also, care should be taken to avoid introducing too much additional combinatorial delay on its outputs. + +\section{WISHBONE instruction bus} + +The \lxp{}C CPU fetches instructions over the WISHBONE bus. Its parameters are defined in the WISHBONE datasheet (Appendix \ref{app:wishbonedatasheet}). For a detailed description of the bus protocol refer to the WISHBONE specification, revision B3. + +With classic WISHBONE handshake decent throughput can be only achieved when the slave is able to terminate cycles asynchronously. It is usually possible only for the simplest slaves, which should probably be using the Low Latency Interface in the first place. To maximize throughput for complex, high latency slaves, \lxp{}C instruction bus uses optional WISHBONE address tags [CTI\_O()] (Cycle Type Identifier) and [BTE\_O()] (Burst Type Extension). These signals are hints allowing the slave to predict the address that will be set by the master in the next cycle and prepare data in advance. The slave can ignore these hints, processing requests as classic WISHBONE cycles, although performance would almost certainly suffer in this case. + +A typical \lxp{}C instruction bus burst timing diagram is shown on Figure \ref{fig:ibustiming}. + +\begin{figure}[htbp] + \centering + \includegraphics[scale=0.786]{images/ibustiming.pdf} + \caption{Typical WISHBONE instruction bus burst (\lxp{}C)} + \label{fig:ibustiming} +\end{figure} + +\section{WISHBONE data bus} + +\lxp{} uses the WISHBONE bus to interact with data memory and other peripherals. This bus is distinct from the instruction bus; its parameters are defined in the WISHBONE datasheet (Appendix \ref{app:wishbonedatasheet}). + +This bus uses a 30-bit \signal{dbus\_adr\_o} port to address 32-bit words; the \signal{dbus\_sel\_o} port is used to select individual bytes to be written or read. Alternatively, with the \code{DBUS\_RMW} option (Section \ref{sec:generics}) the \signal{dbus\_sel\_o} port is not used; byte-granular write access is performed using the read-modify-write cycle instead. + +For a detailed description of the bus protocol refer to the WISHBONE specification, revision B3. + +Typical timing diagrams for write and read cycles are shown on Figure \ref{fig:dbustiming}. In these examples the peripheral terminates the cycle asynchronously; however, it can also introduce wait states by delaying the \signal{dbus\_ack\_i} signal. + +\begin{figure}[htbp] + \centering + \includegraphics[scale=0.928]{images/dbustiming.pdf} + \caption{Typical WISHBONE data bus WRITE and READ cycles} + \label{fig:dbustiming} +\end{figure} + +\section{Interrupts} + +\lxp{} registers an interrupt condition when the corresponding request signal goes from \code{0} to \code{1}. Transitions from \code{1} to \code{0} are ignored. All interrupt request signals must be synchronous with the system clock (\signal{clk\_i}); if coming from an asynchronous source, they must be synchronized using a sequence of at least two flip-flops clocked by \signal{clk\_i}. These flip-flops are not included in the \lxp{} core in order not to increase interrupt processing delay for interrupt sources that are inherently synchronous. Failure to properly synchronize interrupt request signals will cause timing violations that will manifest itself as intermittent, hard to debug faults. + +\section{Synthesis and optimization} +\label{sec:synthesis} + +\subsection{Technology specific primitives} + +\lxp{} RTL design is described in behavioral VHDL. However, it can also benefit from certain special resources provided by most FPGA devices, namely, RAM blocks and dedicated multipliers. For improved portability, hardware description that can potentially be mapped to such resources is localized in separate design units: + +\begin{itemize} + \item \shellcmd{lxp32\_ram256x32} -- a dual-port synchronous $256 \times 32$ bit RAM with one write port and one read port; + \item \shellcmd{lxp32\_mul16x16} -- an unsigned $16 \times 16$ multiplier with an output register. +\end{itemize} + +These design units contain behavioral description of respective hardware that is recognizable by FPGA synthesis tools. Usually no adjustments are needed as the synthesizer will automatically infer an appropriate primitive from its behavioral description. If automatic inference produces unsatisfactory results, these design units can be replaced with library element wrappers. The same is true for ASIC logic synthesis software which is unlikely to infer complex primitives. + +\subsection{General optimization guidelines} + +This subsection contains general advice on achieving satisfactory synthesis results regardless of the optimization goal. Some of these suggestions are also mentioned in other parts of this manual. + +\begin{enumerate} + \item If the technology doesn't provide dedicated multiplier resources, consider using \code{"opt"} or \code{"seq"} multiplier architecture (Section \ref{sec:generics}). + + \item Ensure that the instruction bus has adequate throughput. For \lxp{}C, check that the slave supports the WISHBONE registered feedback signals [CTI\_I()] and [BTE\_I()]. + + \item Multiplexing instruction and data buses, or connecting them to the same interconnect that allows only one master at a time to be active (i.e. \emph{shared bus} interconnect topology) is not recommended. If you absolutely must do so, assign a higher priority level to the data bus, otherwise instruction prefetches will massively slow down data transactions. +\end{enumerate} + +\subsection{Optimizing for timing} + +\begin{enumerate} + \item Set up reasonable timing constraints. Do not overconstrain the design by more that 10--15~\%. + + \item Analyze the worst path. The natural \lxp{} timing bottleneck usually goes from the scratchpad (register file) output through the ALU (in the Execute stage) to the scratchpad input. If timing analysis lists other critical paths, the problem can lie elsewhere. If the \signal{rst\_i} signal becomes a bottleneck, promote it to a global network or, with SRAM-based FPGAs, consider operating without reset (see Section \ref{sec:clockreset}). Critical paths affecting the WISHBONE state machines could indicate problems with interconnect performance. + + \item Configure the synthesis tool to reduce the fanout limit. Note that setting this limit to a too small value can lead to an opposite effect. + + \item Synthesis tools can support additional options to improve timing, such as the \emph{Retiming} algorithm which rearranges registers and combinatorial logic across the pipeline in attempt to balance delays. The efficiency of such algorithms is not very predictable. In general, sloppy designs are the most likely to benefit from it, while for a carefully designed circuit timing can sometimes get worse. +\end{enumerate} + +\subsection{Optimizing for area} + +\begin{enumerate} + \item Consider excluding the divider if not using it (see Section \ref{sec:generics}). + + \item Relaxing timing constraints can sometimes allow the synthesizer to produce a more area-efficient circuit. + + \item Increase the fanout limit in the synthesizer settings to reduce buffer replication. +\end{enumerate} + +\chapter{Simulation} +\label{ch:simulation} + +\lxp{} package includes an automated verification environment (self-checking testbench) which verifies the \lxp{} CPU functional correctness. The environment consists of two major parts: a test platform which is a SoC-like design providing peripherals for the CPU to interact with, and the testbench itself which loads test firmware and monitors the platform's output signals. Like the CPU itself, the test environment is written in VHDL-93. + +A separate testbench for the instruction cache (\shellcmd{lxp32\_icache}) is also provided. It can be invoked similarly to the main CPU testbench. + +\section{Requirements} + +The following software is required to simulate the \lxp{} design: + +\begin{itemize} + \item An HDL simulator supporting VHDL-93. \lxp{} package includes scripts (makefiles) for the following simulators: + + \begin{itemize} + \item GHDL -- a free and open-source VHDL simulator which supports multiple operating systems\footnote{\url{http://ghdl.free.fr/}}; + \item Mentor Graphics\textregistered{} ModelSim\textregistered{} simulator (\shellcmd{vsim}); + \item Xilinx\textregistered{} Vivado\textregistered{} Simulator (\shellcmd{xsim}). + \end{itemize} + + With GHDL, a waveform viewer such as GTKWave is also recommended (Figure \ref{fig:gtkwave})\footnote{\url{http://gtkwave.sourceforge.net/}}. + + Some FPGA vendors provide limited versions of the ModelSim\textregistered{} simulator for free as parts of their design suites. These versions should suffice for \lxp{} simulation. + + Other simulators can be used with some preparations (Section \ref{sec:simmanual}). + + \item GNU \shellcmd{make} and \shellcmd{coreutils} are needed to simulate the design using the provided makefiles. Under Microsoft\textregistered{} Windows\textregistered{}, MSYS or Cygwin can be used. + \item \lxp{} assembler/linker program (\shellcmd{lxp32asm}) must be present (Section \ref{sec:lxp32asm}). A prebuilt executable for Microsoft\textregistered{} Windows\textregistered{} is already included in the \lxp{} package, for other operating systems \shellcmd{lxp32asm} must be built from source (Section \ref{sec:buildfromsource}). +\end{itemize} + +\begin{figure}[htbp] + \centering + \includegraphics[scale=0.65]{images/gtkwave.png} + \caption{GTKWave displaying the \lxp{} waveform dump produced by GHDL} + \label{fig:gtkwave} +\end{figure} + +\section{Running simulation using makefiles} + +To simulate the design, go to the \shellcmd{verify/lxp32/run/<\emph{simulator}>} directory and run \shellcmd{make}. The following make targets are supported: + +\begin{itemize} + \item \shellcmd{batch} -- simulate the design in batch mode. Results will be written to the standard output. This is the default target. + \item \shellcmd{gui} -- simulate the design in GUI mode. Note: since GHDL doesn't have a GUI, the simulation itself will be run in batch mode; upon a successful completion, GTKWave will be run automatically to display dumped waveforms. + \item \shellcmd{compile} -- compile only, don't run simulation. + \item \shellcmd{clean} -- delete all the produced artifacts. +\end{itemize} + +\section{Running simulation manually} +\label{sec:simmanual} + +\lxp{} testbench can be also run manually. The following steps must be performed: + +\begin{enumerate} + \item Compile the test firmware in the \shellcmd{verify/lxp32/src/firmware} directory: + + \begin{codepar} + lxp32asm -f textio \emph{filename}.asm -o \emph{filename}.ram + \end{codepar} + + Produced \shellcmd{*.ram} files must be placed to the simulator's working directory. + \item Compile the \lxp{} RTL description (\shellcmd{rtl} directory). + \item Compile the test platform (\shellcmd{verify/lxp32/src/platform} directory). + \item Compile the testbench itself (\shellcmd{verify/lxp32/src/tb} directory). + \item Simulate the \shellcmd{tb} design unit defined in the \shellcmd{tb.vhd} file. +\end{enumerate} + +\section{Testbench parameters} + +Simulation parameters can be configured by overriding generics defined by the \shellcmd{tb} design unit: + +\begin{itemize} + \item \code{MODEL\_LXP32C} -- simulate the \lxp{}C version. By default, this option is set to \code{true}. If set to \code{false}, \lxp{}U is simulated instead. + \item \code{TEST\_CASE} -- if set to a non-empty string, specifies the file name of a test case to run. If set to an empty string (default), all tests are executed. + \item \code{THROTTLE\_DBUS} -- perform pseudo-random data bus throttling. By default, this option is set to \code{true}. + \item \code{THROTTLE\_IBUS} -- perform pseudo-random instruction bus throttling. By default, this option is set to \code{true}. + \item \code{VERBOSE} -- print more messages. +\end{itemize} + +\chapter{Development tools} +\label{ch:developmenttools} + +\section{\shellcmd{lxp32asm} -- Assembler and linker} +\label{sec:lxp32asm} + +\shellcmd{lxp32asm} is a combined assembler and linker for the \lxp{} platform. It takes one or more input files and produces executable code for the CPU. Input files can be either source files in the \lxp{} assembly language (Appendix \ref{app:assemblylanguage}) or \emph{linkable objects}. Linkable object is a relocatable format for storing compiled \lxp{} code together with symbol information. + +\shellcmd{lxp32asm} operates in two stages: + +\begin{enumerate} + \item Compile. + + Source files are compiled to linkable objects. + + \item Link. + + Linkable objects are combined into a single executable module. References to symbols defined in external modules are resolved at this stage. +\end{enumerate} + +In the simplest case there is only one input source file which doesn't contain external symbol references. If there are multiple input files, one of them must define the \code{entry} symbol at the beginning of the code. + +\subsection{Command line syntax} + +\begin{codepar} + lxp32asm [ \emph{options} | \emph{input files} ] +\end{codepar} + +Options supported by \shellcmd{lxp32asm} are listed below: + +\begin{itemize} + \item \shellcmd{-a \emph{align}} -- section alignment. Must be a multiple of 4, default value is 4. Ignored in compile-only mode. + + \item \shellcmd{-b \emph{addr}} -- Base address, that is, address in memory where the executable image will be located. Must be a multiple of section alignment. Default value is 0. Ignored in compile-only mode. + + \item \shellcmd{-c} -- compile only (skip the Link stage). + + \item \shellcmd{-f \emph{fmt}} -- select executable image format (see below for the list of supported formats). Ignored in compile-only mode. + + \item \shellcmd{-h}, \shellcmd{--help} -- display a short help message and exit. + + \item \shellcmd{-i \emph{dir}} -- add \emph{dir} to the list of directories used to search for included files. Multiple directories can be specified with multiple \shellcmd{-i} arguments. + + \item \shellcmd{-o \emph{file}} -- output file name. + + \item \shellcmd{-s \emph{size}} -- size of the executable image. Must be a multiple of 4. If total code size is less than the specified value, the executable image is padded with zeros. By default, image is not padded. This option is ignored in compile-only mode. + + \item \shellcmd{--} -- do not interpret subsequent command line arguments as options. Can be used if there are input file names starting with dash. +\end{itemize} + +\subsection{Output formats} + +The following output formats are supported by \shellcmd{lxp32asm}: + +\begin{itemize} + \item \shellcmd{bin} -- raw binary image. This is the default format. + \item \shellcmd{textio} -- text format representing binary data as a sequence of zeros and ones. This format can be directly read from VHDL (using the \code{std.textio} package) or Verilog\textregistered{} (using the \code{\$readmemb} function). + \item \shellcmd{dec} -- text format representing each word as a decimal number. + \item \shellcmd{hex} -- text format representing each word as a hexadecimal number. +\end{itemize} + +\section{\shellcmd{lxp32dump} -- Disassembler} + +\shellcmd{lxp32dump} takes an executable image and produces a source file in \lxp{} assembly language. The produced file is a valid program that can be compiled by \shellcmd{lxp32asm}. + +\subsection{Command line syntax} + +\begin{codepar} + lxp32dump [ \emph{options} | \emph{input file} ] +\end{codepar} + +Supported options are: + +\begin{itemize} + \item \shellcmd{-b \emph{addr}} -- executable image base address, only used for comments. + + \item \shellcmd{-f \emph{fmt}} -- input file format. All \shellcmd{lxp32asm} output formats are supported. If this option is not supplied, autodetection is performed. + + \item \shellcmd{-h}, \shellcmd{--help} -- display a short help message and exit. + + \item \shellcmd{-o \emph{file}} -- output file name. By default, the standard output stream is used. + + \item \shellcmd{--} -- do not interpret subsequent command line arguments as options. +\end{itemize} + +\section{\shellcmd{wigen} -- Interconnect generator} + +\shellcmd{wigen} is a small tool that generates VHDL description of a simple WISHBONE interconnect based on shared bus topology. It supports any number of masters and slaves. + +For interconnects with multiple masters a priority-based arbitration circuit is inserted with lower-numbered masters taking precedence. However, when a bus cycle is in progress ([CYC\_O] is asserted by the active master), the arbiter will not interrupt it even if a master with a higher priority level requests bus ownership. + +\subsection{Command line syntax} + +\begin{codepar} + wigen [ \emph{option(s)} ] \emph{nm} \emph{ns} \emph{ma} \emph{sa} \emph{ps} [ \emph{pg} ] +\end{codepar} + +\begin{itemize} + \item\shellcmd{\emph{nm}} -- number of masters, + \item\shellcmd{\emph{ns}} -- number of slaves, + \item\shellcmd{\emph{ma}} -- master address width, + \item\shellcmd{\emph{sa}} -- slave address width, + \item\shellcmd{\emph{ps}} -- port size (8, 16, 32 or 64), + \item\shellcmd{\emph{pg}} -- port granularity (8, 16, 32 or 64, default: the same as port size). +\end{itemize} + +Supported options are: + +\begin{itemize} + \item \shellcmd{-e \emph{entity}} -- name of the design entity (default is \code{"intercon"}). + + \item \shellcmd{-h}, \shellcmd{--help} -- display a short help message and exit. + + \item \shellcmd{-o \emph{file}} -- output file name (default is \shellcmd{\emph{entity}.vhd}). + + \item \shellcmd{-p} -- generate pipelined arbiter (reduced combinatorial delays, increased latency). + + \item \shellcmd{-r} -- generate WISHBONE registered feedback signals ([CTI\_IO()] and [BTE\_IO()]). + + \item \shellcmd{-u} -- generate unsafe slave decoder (reduced combinatorial delays and resource usage, may not work properly if the address is invalid). +\end{itemize} + +\section{Building from source} +\label{sec:buildfromsource} + +Prebuilt tool executables for 32-bit Microsoft\textregistered{} Windows\textregistered{} are included in the \lxp{} IP core package. For other platforms the tools must be built from source. Since they are developed in \cplusplus{} using only the standard library, it should be possible to build them for any platform that provides a modern \cplusplus{} compiler. + +\subsection{Requirements} + +The following software is required to build \lxp{} tools from source: + +\begin{enumerate} + \item A modern \cplusplus{} compiler, such as Microsoft\textregistered{} Visual Studio\textregistered{} 2013 or newer, GCC 4.8 or newer, Clang 3.4 or newer. + \item CMake 3.3 or newer. +\end{enumerate} + +\subsection{Build procedure} + +This software uses CMake as a build system generator. Building it involves two steps: first, the \shellcmd{cmake} program is invoked to generate a native build environment (a set of Makefiles or an IDE project); second, the generated environment is used to build the software. + +\subsubsection{Examples} + +In the following examples, it is assumed that the commands are run from the \shellcmd{tools} subdirectory of the \lxp{} IP core package tree. + +For Microsoft\textregistered{} Visual Studio\textregistered{}: + +\begin{codepar} + mkdir build + cd build + cmake -G "NMake Makefiles" ../src + nmake + nmake install +\end{codepar} + +For MSYS: + +\begin{codepar} + mkdir build + cd build + cmake -G "MSYS Makefiles" ../src + make + make install +\end{codepar} + +For MinGW without MSYS: + +\begin{codepar} + mkdir build + cd build + cmake -G "MinGW Makefiles" ../src + mingw32-make + mingw32-make install +\end{codepar} + +For other platforms: + +\begin{codepar} + mkdir build + cd build + cmake ../src + make + make install +\end{codepar} + +More details can be found in the CMake documentation. + +\appendix + +\chapter{Instruction set reference} +\label{app:instructionset} + +See Section \ref{sec:instructionformat} for a general description of \lxp{} instruction encoding. + +\section{List of instructions by group} + +\begin{ctabular}{lll} + \toprule + Instruction & Description & Opcode \\ + \midrule + \tabcutin{3}{Data transfer} \\ + \midrule + \hyperref[subsec:instr:mov]{\instr{mov}} & Move & alias for \code{\instr{add} dst, src, 0} \\ + \hyperref[subsec:instr:lc]{\instr{lc}} & Load Constant & \code{000001} \\ + \hyperref[subsec:instr:lw]{\instr{lw}} & Load Word & \code{001000} \\ + \hyperref[subsec:instr:lub]{\instr{lub}} & Load Unsigned Byte & \code{001010} \\ + \hyperref[subsec:instr:lsb]{\instr{lsb}} & Load Signed Byte & \code{001011} \\ + \hyperref[subsec:instr:sw]{\instr{sw}} & Store Word & \code{001100} \\ + \hyperref[subsec:instr:sb]{\instr{sb}} & Store Byte & \code{001110} \\ + \midrule + \tabcutin{3}{Arithmetic operations} \\ + \midrule + \hyperref[subsec:instr:add]{\instr{add}} & Add & \code{010000} \\ + \hyperref[subsec:instr:sub]{\instr{sub}} & Subtract & \code{010001} \\ + \hyperref[subsec:instr:mul]{\instr{mul}} & Multiply & \code{010010} \\ + \hyperref[subsec:instr:divu]{\instr{divu}} & Divide Unsigned & \code{010100} \\ + \hyperref[subsec:instr:divs]{\instr{divs}} & Divide Signed & \code{010101} \\ + \hyperref[subsec:instr:modu]{\instr{modu}} & Modulo Unsigned & \code{010110} \\ + \hyperref[subsec:instr:mods]{\instr{mods}} & Modulo Signed & \code{010111} \\ + \midrule + \tabcutin{3}{Bitwise operations} \\ + \midrule + \hyperref[subsec:instr:not]{\instr{not}} & Bitwise Not & alias for \code{\instr{xor} dst, src, -1} \\ + \hyperref[subsec:instr:and]{\instr{and}} & Bitwise And & \code{011000} \\ + \hyperref[subsec:instr:or]{\instr{or}} & Bitwise Or & \code{011001} \\ + \hyperref[subsec:instr:xor]{\instr{xor}} & Bitwise Exclusive Or & \code{011010}\\ + \hyperref[subsec:instr:sl]{\instr{sl}} & Shift Left & \code{011100} \\ + \hyperref[subsec:instr:sru]{\instr{sru}} & Shift Right Unsigned & \code{011110} \\ + \hyperref[subsec:instr:srs]{\instr{srs}} & Shift Right Signed & \code{011111} \\ + \midrule + \tabcutin{3}{Execution transfer} \\ + \midrule + \hyperref[subsec:instr:jmp]{\instr{jmp}} & Jump & \code{100000} \\ + \hyperref[subsec:instr:cjmpxxx]{\instr{cjmp\emph{xxx}}} & Compare and Jump & \code{11\emph{xxxx}} (\code{\emph{xxxx}} = condition) \\ + \hyperref[subsec:instr:call]{\instr{call}} & Call Procedure & \code{100001} \\ + \hyperref[subsec:instr:ret]{\instr{ret}} & Return from Procedure & alias for \code{\instr{jmp} rp} \\ + \hyperref[subsec:instr:iret]{\instr{iret}} & Interrupt Return & alias for \code{\instr{jmp} irp}\\ + \midrule + \tabcutin{3}{Miscellaneous instructions} \\ + \midrule + \hyperref[subsec:instr:nop]{\instr{nop}} & No Operation & \code{000000} \\ + \hyperref[subsec:instr:hlt]{\instr{hlt}} & Halt & \code{000010} \\ +\end{ctabular} + +\section{Alphabetical list of instructions} + +\settocdepth{subsection} + +{ +\setlength{\parindent}{0pt} +\nonzeroparskip + +\subsection{\instr{add} -- Add} +\label{subsec:instr:add} + +\subsubsection{Syntax} + +\code{\instr{add} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{010000 T1 T2 DST RD1 RD2} + +Example: \code{\instr{add} r2, r1, 10} $\rightarrow$ \code{0x4202010A} + +\subsubsection{Operation} + +\code{DST := RD1 + RD2} + +\subsection{\instr{and} -- Bitwise And} +\label{subsec:instr:and} + +\subsubsection{Syntax} + +\code{\instr{and} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{011000 T1 T2 DST RD1 RD2} + +Example: \code{\instr{and} r2, r1, 0x3F} $\rightarrow$ \code{0x6202013F} + +\subsubsection{Operation} + +\code{DST := RD1 $\land$ RD2} + +\subsection{\instr{call} -- Call Procedure} +\label{subsec:instr:call} + +Save a pointer to the next instruction in the \code{rp} register and transfer execution to the address pointed by the operand. + +\subsubsection{Syntax} + +\code{\instr{call} RD1} + +\subsubsection{Encoding} + +\code{100001 1 0 11111110 RD1 00000000} + +RD1 must be a register. + +Example: \code{\instr{call} r1} $\rightarrow$ \code{0x86FE0100} + +\subsubsection{Operation} + +\code{rp := \emph{return\_address}} + +\code{goto RD1} + +Pointer in RD1 is interpreted as described in Section \ref{sec:addressing}. + +\subsection{\instr{cjmp\emph{xxx}} -- Compare and Jump} +\label{subsec:instr:cjmpxxx} + +Compare two operands and transfer execution to the specified address if a condition is satisfied. + +\subsubsection{Syntax} + +\code{\instr{cjmpe} DST, RD1, RD2} (Equal) + +\code{\instr{cjmpne} DST, RD1, RD2} (Not Equal) + +\code{\instr{cjmpsg} DST, RD1, RD2} (Signed Greater) + +\code{\instr{cjmpsge} DST, RD1, RD2} (Signed Greater or Equal) + +\code{\instr{cjmpsl} DST, RD1, RD2} (Signed Less) + +\code{\instr{cjmpsle} DST, RD1, RD2} (Signed Less or Equal) + +\code{\instr{cjmpug} DST, RD1, RD2} (Unsigned Greater) + +\code{\instr{cjmpuge} DST, RD1, RD2} (Unsigned Greater or Equal) + +\code{\instr{cjmpul} DST, RD1, RD2} (Unsigned Less) + +\code{\instr{cjmpule} DST, RD1, RD2} (Unsigned Less or Equal) + +\subsubsection{Encoding} + +\code{OPCODE T1 T2 DST RD1 RD2} + +Opcodes: + +\begin{tabularx}{\textwidth}{lL} +\instr{cjmpe} & \code{111000} \\ +\instr{cjmpne} & \code{110100} \\ +\instr{cjmpsg} & \code{110001} \\ +\instr{cjmpsge} & \code{111001} \\ +\instr{cjmpug} & \code{110010} \\ +\instr{cjmpuge} & \code{111010} \\ +\end{tabularx} + +\instr{cjmpsl}, \instr{cjmpsle}, \instr{cjmpul}, \instr{cjmpule} are aliases for \instr{cjmpsg}, \instr{cjmpsge}, \instr{cjmpug}, \instr{cjmpuge}, respectively, with RD1 and RD2 operands swapped. + +Example: \code{\instr{cjmpuge} r2, r1, 5} $\rightarrow$ \code{0xEA020105} + +\subsubsection{Operation} + +\code{if \emph{condition} then goto DST} + +Pointer in DST is interpreted as described in Section \ref{sec:addressing}. Unlike most instructions, \instr{cjmp\emph{xxx}} does not write to DST. + +\subsection{\instr{divs} -- Divide Signed} +\label{subsec:instr:divs} + +\subsubsection{Syntax} + +\code{\instr{divs} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{010101 T1 T2 DST RD1 RD2} + +Example: \code{\instr{divs} r2, r1, -3} $\rightarrow$ \code{0x560201FD} + +\subsubsection{Operation} + +\code{DST := (\emph{signed}) RD1 / (\emph{signed}) RD2} + +The result is rounded towards zero and is undefined if RD2 is zero. If the CPU was configured without a divider, this instruction returns \code{0}. + +\subsection{\instr{divu} -- Divide Unsigned} +\label{subsec:instr:divu} + +\subsubsection{Syntax} + +\code{\instr{divu} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{010100 T1 T2 DST RD1 RD2} + +Example: \code{\instr{divu} r2, r1, 73} $\rightarrow$ \code{0x52020107} + +\subsubsection{Operation} + +\code{DST := RD1 / RD2} + +The result is rounded towards zero and is undefined if RD2 is zero. If the CPU was configured without a divider, this instruction returns \code{0}. + +\subsection{\instr{hlt} -- Halt} +\label{subsec:instr:hlt} + +Wait for an interrupt. + +\subsubsection{Syntax} + +\code{\instr{hlt}} + +\subsubsection{Encoding} + +\code{000010 0 0 00000000 00000000 00000000} + +\subsubsection{Operation} + +Pause execution until an interrupt is received. + +\subsection{\instr{jmp} -- Jump} +\label{subsec:instr:jmp} + +Transfer execution to the address pointed by the operand. + +\subsubsection{Syntax} + +\code{\instr{jmp} RD1} + +\subsubsection{Encoding} + +\code{100000 1 0 00000000 RD1 00000000} + +RD1 must be a register. + +Example: \code{\instr{jmp} r1} $\rightarrow$ \code{0x82000100} + +\subsubsection{Operation} + +\code{goto RD1} + +Pointer in RD1 is interpreted as described in Section \ref{sec:addressing}. + +\subsection{\instr{iret} -- Interrupt Return} +\label{subsec:instr:iret} + +Return from an interrupt handler. + +\subsubsection{Syntax} + +\instr{iret} + +Alias for \code{\instr{jmp} irp}. + +\subsection{\instr{lc} -- Load Constant} +\label{subsec:instr:lc} + +Load a 32-bit word to the specified register. Note that values in the [-128; 127] range can be loaded more efficiently using the \instr{mov} instruction alias. + +\subsubsection{Syntax} + +\code{\instr{lc} DST, WORD32} + +\subsubsection{Encoding} + +\code{000001 0 0 DST 00000000 00000000 WORD32} + +Unlike other instructions, \instr{lc} occupies two 32-bit words. + +Example: \code{\instr{lc} r1, 0x12345678} $\rightarrow$ \code{0x04010000 0x12345678} + +\subsubsection{Operation} + +\code{DST := WORD32} + +\subsection{\instr{lsb} -- Load Signed Byte} +\label{subsec:instr:lsb} + +Load a byte from the specified address to the register, performing sign extension. + +\subsubsection{Syntax} + +\code{\instr{lsb} DST, RD1} + +\subsubsection{Encoding} + +\code{001011 1 0 DST RD1 00000000} + +RD1 must be a register. + +Example: \code{\instr{lsb} r2, r1} $\rightarrow$ \code{0x2E020100} + +\subsubsection{Operation} + +\code{DST := (\emph{signed}) (*(BYTE*)RD1)} + +Pointer in RD1 is interpreted as described in Section \ref{sec:addressing}. + +\subsection{\instr{lub} -- Load Unsigned Byte} +\label{subsec:instr:lub} + +Load a byte from the specified address to the register. Higher 24 bits are zeroed. + +\subsubsection{Syntax} + +\code{\instr{lub} DST, RD1} + +\subsubsection{Encoding} + +\code{001010 1 0 DST RD1 00000000} + +RD1 must be a register. + +Example: \code{\instr{lub} r2, r1} $\rightarrow$ \code{0x2A020100} + +\subsubsection{Operation} + +\code{DST := *(BYTE*)RD1} + +Pointer in RD1 is interpreted as described in Section \ref{sec:addressing}. + +\subsection{\instr{lw} -- Load Word} +\label{subsec:instr:lw} + +Load a word from the specified address to the register. + +\subsubsection{Syntax} + +\code{\instr{lw} DST, RD1} + +\subsubsection{Encoding} + +\code{001000 1 0 DST RD1 00000000} + +RD1 must be a register. + +Example: \code{\instr{lw} r2, r1} $\rightarrow$ \code{0x22020100} + +\subsubsection{Operation} + +\code{DST := *RD1} + +Pointer in RD1 is interpreted as described in Section \ref{sec:addressing}. + +\subsection{\instr{mods} -- Modulo Signed} +\label{subsec:instr:mods} + +\subsubsection{Syntax} + +\code{\instr{mods} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{010111 T1 T2 DST RD1 RD2} + +Example: \code{\instr{mods} r2, r1, 10} $\rightarrow$ \code{0x5E02010A} + +\subsubsection{Operation} + +\code{DST := (\emph{signed}) RD1 mod (\emph{signed}) RD2} + +Modulo operation satisfies the following condition: if $Q=A/B$ and $R=A \mod B$, then $A=B \cdot Q+R$. + +The result is undefined if RD2 is zero. If the CPU was configured without a divider, this instruction returns \code{0}. + +\subsection{\instr{modu} -- Modulo Unsigned} +\label{subsec:instr:modu} + +\subsubsection{Syntax} + +\code{\instr{modu} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{010110 T1 T2 DST RD1 RD2} + +Example: \code{\instr{modu} r2, r1, 10} $\rightarrow$ \code{0x5A02010A} + +\subsubsection{Operation} + +\code{DST := RD1 mod RD2} + +Modulo operation satisfies the following condition: if $Q=A/B$ and $R=A \mod B$, then $A=B \cdot Q+R$. + +The result is undefined if RD2 is zero. If the CPU was configured without a divider, this instruction returns \code{0}. + +\subsection{\instr{mov} -- Move} +\label{subsec:instr:mov} + +\subsubsection{Syntax} + +\code{\instr{mov} DST, RD1} + +Alias for \code{\instr{add} DST, RD1, 0} + +\subsection{\instr{mul} -- Multiply} +\label{subsec:instr:mul} + +Multiply two 32-bit values. The result is also 32-bit. + +\subsubsection{Syntax} + +\code{\instr{mul} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{010010 T1 T2 DST RD1 RD2} + +Example: \code{\instr{mul} r2, r1, 3} $\rightarrow$ \code{0x4A020103} + +\subsubsection{Operation} + +\code{DST := RD1 * RD2} + +Since the product width is the same as the operand width, the result of a multiplication does not depend on operand signedness. + +\subsection{\instr{nop} -- No Operation} +\label{subsec:instr:nop} + +\subsubsection{Syntax} + +\instr{nop} + +\subsubsection{Encoding} + +\code{000000 0 0 00000000 00000000 00000000} + +\subsubsection{Operation} + +This instruction does not alter the machine state. + +\subsection{\instr{not} -- Bitwise Not} +\label{subsec:instr:not} + +\subsubsection{Syntax} + +\code{\instr{not} DST, RD1} + +Alias for \code{\instr{xor} DST, RD1, -1}. + +\subsection{\instr{or} -- Bitwise Or} +\label{subsec:instr:or} + +\subsubsection{Syntax} + +\code{\instr{or} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{011001 T1 T2 DST RD1 RD2} + +Example: \code{\instr{or} r2, r1, 0x3F} $\rightarrow$ \code{0x6602013F} + +\subsubsection{Operation} + +\code{DST := RD1 $\lor$ RD2} + +\subsection{\instr{ret} -- Return from Procedure} +\label{subsec:instr:ret} + +Return from a procedure. + +\subsubsection{Syntax} + +\instr{ret} + +Alias for \code{\instr{jmp} rp}. + +\subsection{\instr{sb} -- Store Byte} +\label{subsec:instr:sb} + +Store the lowest byte from the register to the specified address. + +\subsubsection{Syntax} + +\code{\instr{sb} RD1, RD2} + +\subsubsection{Encoding} + +\code{001110 1 T2 00000000 RD1 RD2} + +RD1 must be a register. + +Example: \code{\instr{sb} r2, r1} $\rightarrow$ \code{0x3B000201} + +\subsubsection{Operation} + +\code{*(BYTE*)RD1 := RD2 $\land$ 0x000000FF} + +Pointer in RD1 is interpreted as described in Section \ref{sec:addressing}. + +\subsection{\instr{sl} -- Shift Left} +\label{subsec:instr:sl} + +\subsubsection{Syntax} + +\code{\instr{sl} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{011100 T1 T2 DST RD1 RD2} + +Example: \code{\instr{sl} r2, r1, 5} $\rightarrow$ \code{0x72020105} + +\subsubsection{Operation} + +\code{DST := RD1 << RD2} + +The result is undefined if RD2 is outside the [0; 31] range. + +\subsection{\instr{srs} -- Shift Right Signed} +\label{subsec:instr:srs} + +\subsubsection{Syntax} + +\code{\instr{srs} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{011111 T1 T2 DST RD1 RD2} + +Example: \code{\instr{srs} r2, r1, 5} $\rightarrow$ \code{0x7E020105} + +\subsubsection{Operation} + +\code{DST := ((\emph{signed}) RD1) >> RD2} + +The result is undefined if RD2 is outside the [0; 31] range. + +\subsection{\instr{sru} -- Shift Right Unsigned} +\label{subsec:instr:sru} + +\subsubsection{Syntax} + +\code{\instr{sru} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{011110 T1 T2 DST RD1 RD2} + +Example: \code{\instr{sru} r2, r1, 5} $\rightarrow$ \code{0x7A020105} + +\subsubsection{Operation} + +\code{DST := RD1 >> RD2} + +The result is undefined if RD2 is outside the [0; 31] range. + +\subsection{\instr{sub} -- Subtract} +\label{subsec:instr:sub} + +\subsubsection{Syntax} + +\code{\instr{sub} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{010001 T1 T2 DST RD1 RD2} + +Example: \code{\instr{sub} r2, r1, 5} $\rightarrow$ \code{0x46020105} + +\subsubsection{Operation} + +\code{DST := RD1 - RD2} + +\subsection{\instr{sw} -- Store Word} +\label{subsec:instr:sw} + +Store the value of the register to the specified address. + +\subsubsection{Syntax} + +\code{\instr{sw} RD1, RD2} + +\subsubsection{Encoding} + +\code{001100 1 T2 00000000 RD1 RD2} + +RD1 must be a register. + +Example: \code{\instr{sw} r2, r1} $\rightarrow$ \code{0x33000201} + +\subsubsection{Operation} + +\code{*RD1 := RD2} + +Pointer in RD1 is interpreted as described in Section \ref{sec:addressing}. + +\subsection{\instr{xor} -- Bitwise Exclusive Or} +\label{subsec:instr:xor} + +\subsubsection{Syntax} + +\code{\instr{xor} DST, RD1, RD2} + +\subsubsection{Encoding} + +\code{011010 T1 T2 DST RD1 RD2} + +Example: \code{\instr{xor} r2, r1, 0x3F} $\rightarrow$ \code{0x6A02013F} + +\subsubsection{Operation} + +\code{DST := RD1 $\oplus$ RD2} + +} + +\settocdepth{section} + +\chapter{Instruction cycle counts} + +Cycle counts for \lxp{} instructions are listed in Table \ref{tab:cycles}. These values can change in future hardware revisions. + +\begin{table}[htbp] + \centering + \caption{Instruction cycle counts} + \label{tab:cycles} + \begin{tabularx}{0.8\textwidth}{LLLL} + \toprule + Instruction & Cycle count & Instruction & Cycle count \\ + \midrule + \instr{add} & 1 & \instr{modu} & 37 \\ + \instr{and} & 1 & \instr{mov} & 1 \\ + \instr{call} & $\ge$ 4\footnotemark[1] & \instr{mul} & 2, 6 or 34\footnotemark[3] \\ + \instr{cjmp\emph{xxx}} & $\ge$ 5\footnotemark[1] & \instr{nop} & 1 \\ + \instr{divs} & 37 & \instr{not} & 1 \\ + \instr{divu} & 37 & \instr{or} & 1 \\ + \instr{hlt} & N/A & \instr{ret} & $\ge$ 4\footnotemark[1] \\ + \instr{jmp} & $\ge$ 4\footnotemark[1] & \instr{sb} & $\ge$ 2\footnotemark[2] \\ + \instr{iret} & $\ge$ 4\footnotemark[1] & \instr{sl} & 2 \\ + \instr{lc} & 2 & \instr{srs} & 2 \\ + \instr{lsb} & $\ge$ 3\footnotemark[2] & \instr{sru} & 2 \\ + \instr{lub} & $\ge$ 3\footnotemark[2] & \instr{sub} & 1 \\ + \instr{lw} & $\ge$ 3\footnotemark[2] & \instr{sw} & $\ge$ 2\footnotemark[2] \\ + \instr{mods} & 37 & \instr{xor} & 1 \\ + \bottomrule + \end{tabularx} +\end{table} + +\footnotetext[1]{Depends on instruction bus latency. Includes pipeline flushing overhead.} +\footnotetext[2]{Depends on data bus latency.} +\footnotetext[3]{Depends on multiplier architecture set with the \code{MUL\_ARCH} generic. See Section \ref{sec:generics}.} + +\chapter{LXP32 assembly language} +\label{app:assemblylanguage} + +This appendix defines the assembly language used by \lxp{} development tools. + +\section{Comments} + +\lxp{} assembly language supports C style comments that can span across multiple lines and single-line \cplusplus{} style comments: + +\begin{codepar}\itshape + /* + * This is a comment. + */ + + // This is also a comment +\end{codepar} + +From a parser's point of view comments are equivalent to whitespace. + +\section{Literals} + +\lxp{} assembly language uses numeric and string literals similar to those provided by the C programming language. + +Numeric literals can take form of decimal, hexadecimal or octal numbers. Literals prefixed with \code{0x} are interpreted as hexadecimal, literals prefixed with \code{0} are interpreted as octal, other literals are interpreted as decimal. A numeric literal can also start with an unary plus or minus sign which is also considered a part of the literal. + +String literals must be enclosed in double quotes. The most common escape sequences used in C are supported (Table \ref{tab:stringescape}). + +\begin{table}[htbp] + \caption{Escape sequences used in string literals} + \label{tab:stringescape} + \begin{tabularx}{\textwidth}{lL} + \toprule + Sequence & Interpretation \\ + \midrule + \code{\textbackslash\textbackslash} & Backslash character \\ + \code{\textbackslash "} & Double quotation mark \\ + \code{\textbackslash '} & Single quotation mark (can be also used directly) \\ + \code{\textbackslash t} & Tabulation character \\ + \code{\textbackslash n} & Line feed \\ + \code{\textbackslash r} & Carriage return \\ + \code{\textbackslash x\emph{XX}} & Character with a hexadecimal code of \emph{XX} (1--2 digits) \\ + \code{\textbackslash \emph{XXX}} & Character with an octal code of \emph{XXX} (1--3 digits) \\ + \bottomrule + \end{tabularx} +\end{table} + +\section{Symbols} +\label{sec:symbols} + +Symbols are used to refer to data or code locations. \lxp{} assembly language does not have distinct code labels and variable declarations: symbols are used in both these contexts. + +Symbol names must be valid identifiers. A valid identifier must start with an alphabetic character or an underscore, and may contain alphanumeric characters and underscores. + +A symbol definition must be the first token in a source code line followed by a colon. A symbol definition can occupy a separate line (in which case it refers to the following statement). Alternatively, a statement can follow the symbol definition on the same line. + +A special \code{entry} symbol is used to inform the linker about program entry point if there are multiple input files. If defined, this symbol must precede the first instruction or data definition statement in the module. + +Symbols can be used as operands to the \instr{lc} instruction statement. A symbol reference can end with a \code{@\emph{n}} sequence, where \code{\emph{n}} is a numeric literal; in this case it is interpreted as an offset (in bytes) relative to the symbol definition. To refer to symbols defined in other modules, they must first be declared external using the \instr{\#extern} directive. + +\begin{codeparbreakable} + \instr{lc} r10, jump\_label + \instr{lc} r11, data\_word +\emph{// ...} + \instr{sw} r11, r0 \emph{// store the value of r0 to the} + \emph{// location pointed by data\_word} + \instr{jmp} r10 \emph{// transfer execution to jump\_label} +\emph{// ...} +jump\_label: + \instr{mov} r1, r0 +\emph{// ...} +data\_word: + \instr{.word} 0x12345678 +\end{codeparbreakable} + +\section{Statements} + +Each statement occupies a single source code line. There are three kinds of statements: + +\begin{itemize} + \item \emph{Directives} provide directions for the assembler that do not directly cause code generation. + \item \emph{Data definition statements} insert arbitrary data to the generated code. + \item \emph{Instruction statements} insert \lxp{} CPU instructions to the generated code. +\end{itemize} + +\subsection{Directives} + +The first token of a directive statement always starts with the \code{\#} character. + +\begin{codepar} +\instr{\#define} \emph{identifier} \emph{token} [ \emph{token} ... ] +\end{codepar} + +Defines a macro that will be substituted with one or more tokens. The \code{\emph{identifier}} must satisfy the requirements listed in Section \ref{sec:symbols}. Tokens can be anything, including keywords, identifiers, literals and separators (i.e. comma and colon characters). + +\begin{codepar} +\instr{\#extern} \emph{identifier} +\end{codepar} + +Declares \code{\emph{identifier}} as an external symbol. Used to refer to symbols defined in other modules. + +\begin{codepar} +\instr{\#include} \emph{filename} +\end{codepar} + +Processes \code{\emph{filename}} contents as it were literally inserted at the point of the \instr{\#include} directive. \code{\emph{filename}} must be a string literal. + +\begin{codepar} +\instr{\#message} \emph{msg} +\end{codepar} + +Prints \code{\emph{msg}} to the standard output stream. \code{\emph{msg}} must be a string literal. + +\subsection{Data definition statements} + +The first token of a data definition statement always starts with the \code{.} (period) character. + +\begin{codepar} +\instr{.align} [ \emph{alignment} ] +\end{codepar} + +Ensures that code generated by the next data definition or instruction statement is aligned to a multiple of \code{\emph{alignment}} bytes, inserting padding zeros if needed. Default \code{\emph{alignment}} is 4. Instructions and words are always at least word-aligned; the \instr{.align} statement can be used to align them to a larger boundary, or to align byte data (see below). + +\begin{codepar} +\instr{.byte} \emph{token} [, \emph{token} ... ] +\end{codepar} + +Inserts one or more bytes to the output code. Each \code{\emph{token}} can be either a numeric literal with a valid range of [-128; 255] or a string literal. By default, bytes are not aligned. + +\begin{codepar} +\instr{.reserve} \emph{n} +\end{codepar} + +Inserts \code{\emph{n}} zero bytes to the output code. + +\begin{codepar} +\instr{.word} \emph{token} [, \emph{token} ... ] +\end{codepar} + +Inserts one or more 32-bit words to the output code. Tokens must be numeric literals. + +\subsection{Instruction statements} + +Instruction statements have the following general syntax: + +\begin{codepar} + \instr{\emph{instruction}} [ \emph{operand} [, \emph{operand} ... ] ] +\end{codepar} + +Depending on the instruction, operands can be registers, numeric literals or symbols. Supported instructions are listed in Appendix \ref{app:instructionset}. + +\chapter{WISHBONE datasheet} +\label{app:wishbonedatasheet} + +\section[Instruction bus (LXP32C only)]{Instruction bus (\lxp{}C only)} + +\begin{ctabular}{ll} + \toprule + \tabcutin{2}{\makebox[0.9\textwidth][c]{General information}} \\ + \midrule + WISHBONE revision & B3 \\ + Type of interface & MASTER \\ + Supported cycles & BLOCK READ \\ + \midrule + \tabcutin{2}{Signal names} \\ + \midrule + \signal{clk\_i} & CLK\_I \\ + \signal{rst\_i} & RST\_I \\ + \signal{ibus\_cyc\_o} & CYC\_O \\ + \signal{ibus\_stb\_o} & STB\_O \\ + \signal{ibus\_cti\_o} & CTI\_O() \\ + \signal{ibus\_bte\_o} & BTE\_O() \\ + \signal{ibus\_ack\_i} & ACK\_I \\ + \signal{ibus\_adr\_o} & ADR\_O() \\ + \signal{ibus\_dat\_i} & DAT\_I() \\ + \midrule + \tabcutin{2}{Supported tag signals} \\ + \midrule + \signal{ibus\_cti\_o} & Cycle Type Identifier (address tag) \\ + & \hspace{\parindent} ``010'' (Incrementing burst cycle) \\ + & \hspace{\parindent} ``111'' (End-of-Burst) \\ + \signal{ibus\_bte\_o} & Burst Type Extension (address tag) \\ + & \hspace{\parindent} ``00'' (Linear burst) \\ + \midrule + \tabcutin{2}{Dimensions} \\ + \midrule + Port size & 32 \\ + Port granularity & 32 \\ + Maximum operand size & 32 \\ + Data transfer ordering & BIG/LITTLE ENDIAN \\ + Data transfer sequence & UNDEFINED \\ + \bottomrule +\end{ctabular} + +\section{Data bus} + +\begin{ctabular}{ll} + \toprule + \tabcutin{2}{\makebox[0.9\textwidth][c]{General information}} \\ + \midrule + WISHBONE revision & B3 \\ + Type of interface & MASTER \\ + Supported cycles & SINGLE READ/WRITE \\ + & RMW \\ + \midrule + \tabcutin{2}{Signal names} \\ + \midrule + \signal{clk\_i} & CLK\_I \\ + \signal{rst\_i} & RST\_I \\ + \signal{dbus\_cyc\_o} & CYC\_O \\ + \signal{dbus\_stb\_o} & STB\_O \\ + \signal{dbus\_we\_o} & WE\_O \\ + \signal{dbus\_sel\_o} & SEL\_O() \\ + \signal{dbus\_ack\_i} & ACK\_I \\ + \signal{dbus\_adr\_o} & ADR\_O() \\ + \signal{dbus\_dat\_o} & DAT\_O() \\ + \signal{dbus\_dat\_i} & DAT\_I() \\ + \midrule + \tabcutin{2}{Dimensions} \\ + \midrule + Port size & 32 \\ + Port granularity & 8 \\ + Maximum operand size & 32 \\ + Data transfer ordering & LITTLE ENDIAN \\ + Data transfer sequence & UNDEFINED \\ + \bottomrule +\end{ctabular} + +\end{document} Index: lxp32/tags/1.0/doc/lxp32-logo.png =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: lxp32/tags/1.0/doc/lxp32-logo.png =================================================================== --- lxp32/tags/1.0/doc/lxp32-logo.png (nonexistent) +++ lxp32/tags/1.0/doc/lxp32-logo.png (revision 5)
lxp32/tags/1.0/doc/lxp32-logo.png Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: lxp32/tags/1.0/.gitattributes =================================================================== --- lxp32/tags/1.0/.gitattributes (nonexistent) +++ lxp32/tags/1.0/.gitattributes (revision 5) @@ -0,0 +1,2 @@ +# Convert line endings for text files on Windows +* text=auto Index: lxp32/tags/1.0/misc/highlight/notepad++/LXP32Assembly.xml =================================================================== --- lxp32/tags/1.0/misc/highlight/notepad++/LXP32Assembly.xml (nonexistent) +++ lxp32/tags/1.0/misc/highlight/notepad++/LXP32Assembly.xml (revision 5) @@ -0,0 +1,64 @@ + + + + + + + + 00// 01 02 03/* 04*/ + + 0x + A B C D E F a b c d e f + + + + + @ , + + + + + + + + + + + add and call cjmpe cjmpne cjmpsg cjmpsge cjmpsl cjmpsle cjmpug cjmpuge cjmpul cjmpule divs divu hlt jmp iret lc lsb lub lw mods modu mov mul nop not or ret sb sl srs sru sub sw xor + cr irp iv0 iv1 iv2 iv3 iv4 iv5 iv6 iv7 r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r17 r18 r19 r20 r21 r22 r23 r24 r25 r26 r27 r28 r29 r30 r31 r32 r33 r34 r35 r36 r37 r38 r39 r40 r41 r42 r43 r44 r45 r46 r47 r48 r49 r50 r51 r52 r53 r54 r55 r56 r57 r58 r59 r60 r61 r62 r63 r64 r65 r66 r67 r68 r69 r70 r71 r72 r73 r74 r75 r76 r77 r78 r79 r80 r81 r82 r83 r84 r85 r86 r87 r88 r89 r90 r91 r92 r93 r94 r95 r96 r97 r98 r99 r100 r101 r102 r103 r104 r105 r106 r107 r108 r109 r110 r111 r112 r113 r114 r115 r116 r117 r118 r119 r120 r121 r122 r123 r124 r125 r126 r127 r128 r129 r130 r131 r132 r133 r134 r135 r136 r137 r138 r139 r140 r141 r142 r143 r144 r145 r146 r147 r148 r149 r150 r151 r152 r153 r154 r155 r156 r157 r158 r159 r160 r161 r162 r163 r164 r165 r166 r167 r168 r169 r170 r171 r172 r173 r174 r175 r176 r177 r178 r179 r180 r181 r182 r183 r184 r185 r186 r187 r188 r189 r190 r191 r192 r193 r194 r195 r196 r197 r198 r199 r200 r201 r202 r203 r204 r205 r206 r207 r208 r209 r210 r211 r212 r213 r214 r215 r216 r217 r218 r219 r220 r221 r222 r223 r224 r225 r226 r227 r228 r229 r230 r231 r232 r233 r234 r235 r236 r237 r238 r239 r240 r241 r242 r243 r244 r245 r246 r247 r248 r249 r250 r251 r252 r253 r254 r255 rp sp + #define #extern #include #message + .align .byte .reserve .word + + + + + 00" 01\ 02" 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: lxp32/tags/1.0/misc/highlight/akelpad/asm.coder =================================================================== Cannot display: file marked as a binary type. svn:mime-type = application/octet-stream Index: lxp32/tags/1.0/misc/highlight/akelpad/asm.coder =================================================================== --- lxp32/tags/1.0/misc/highlight/akelpad/asm.coder (nonexistent) +++ lxp32/tags/1.0/misc/highlight/akelpad/asm.coder (revision 5)
lxp32/tags/1.0/misc/highlight/akelpad/asm.coder Property changes : Added: svn:mime-type ## -0,0 +1 ## +application/octet-stream \ No newline at end of property Index: lxp32/tags/1.0/misc/highlight/readme.txt =================================================================== --- lxp32/tags/1.0/misc/highlight/readme.txt (nonexistent) +++ lxp32/tags/1.0/misc/highlight/readme.txt (revision 5) @@ -0,0 +1,5 @@ +This directory contains LXP32 assembly language syntax highlighting +rules for the following text editors: + + * AkelPad (http://akelpad.sourceforge.net/) + * Notepad++ (https://notepad-plus-plus.org/) Index: lxp32/tags/1.0/README.md =================================================================== --- lxp32/tags/1.0/README.md (nonexistent) +++ lxp32/tags/1.0/README.md (revision 5) @@ -0,0 +1,5 @@ +# lxp32-cpu + +A lightweight, open source 32-bit CPU core optimized for FPGA implementation. + +Project website: [https://lxp32.github.io/](https://lxp32.github.io/)

powered by: WebSVN 2.1.0

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