edacation
Version:
Library and CLI for interacting with Yosys and nextpnr.
158 lines • 6.61 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const promises_1 = require("fs/promises");
const path_1 = __importDefault(require("path"));
const helpers_1 = require("yargs/helpers");
const yargs_1 = __importDefault(require("yargs/yargs"));
const index_js_1 = require("../project/index.js");
const util_js_1 = require("../util.js");
const tool_js_1 = require("./tool.js");
const util_js_2 = require("./util.js");
// eslint-disable-next-line @typescript-eslint/no-floating-promises
(async () => {
console.log('EDAcation CLI');
console.log();
const buildCommandArgs = (yargs) => {
return yargs
.positional('project', {
type: 'string',
description: 'EDA project file (e.g. "full-adder.edaproject")'
})
.positional('target', {
type: 'string',
description: 'EDA target'
})
.option('execute', {
type: 'boolean',
description: 'Whether to execute the tool.',
default: true,
alias: 'x'
});
};
// Parse arguments
const argv = await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
.scriptName('edacation')
.command('init <project>', 'Initialize EDA project', (yargs) => {
return yargs
.positional('project', {
type: 'string',
description: 'EDA project file (e.g. "full-adder.edaproject")'
})
.option('name', {
type: 'string',
description: 'Name of the project (e.g. "Full Adder")'
});
})
.command('yosys <project> <target>', 'Synthesize with Yosys', buildCommandArgs)
.command('nextpnr <project> <target>', 'Place and route with nextpnr', buildCommandArgs)
.demandCommand()
.recommendCommands()
.strict()
.help()
.parse();
const command = argv._[0];
// Validate project
const cwdPath = path_1.default.resolve(process.cwd());
let projectFile = argv.project;
let projectPath = path_1.default.join(cwdPath, projectFile);
if (command === 'init') {
if (!projectPath.endsWith('.edaproject')) {
projectFile = `${projectFile}.edaproject`;
projectPath = `${projectPath}.edaproject`;
}
if (await (0, util_js_2.exists)(projectPath)) {
console.error(`Project "${projectFile}" already exists in "${cwdPath}".`);
process.exit(1);
}
let name = argv.name;
if (!name) {
name = path_1.default.basename(projectPath);
name = name.substring(0, name.length - '.edaproject'.length);
}
const project = new index_js_1.Project(name);
await (0, promises_1.writeFile)(projectPath, index_js_1.Project.storeToData(project));
console.log(`Created project "${name}" in "${projectPath}".`);
process.exit(0);
}
if (!(await (0, util_js_2.exists)(projectPath))) {
if (projectPath.endsWith('.edaproject')) {
console.error(`Project "${projectFile}" could not be found in "${cwdPath}".`);
process.exit(1);
}
else {
projectPath = `${projectPath}.edaproject`;
if (!(await (0, util_js_2.exists)(projectPath))) {
console.error(`Project "${projectFile}" or "${projectFile}.edaproject" could not be found in "${cwdPath}".`);
process.exit(1);
}
}
}
const cwd = path_1.default.dirname(projectPath);
const shouldExecute = argv.execute;
const targetId = argv.target;
// Load project
const project = index_js_1.Project.loadFromData(await (0, promises_1.readFile)(projectPath));
const configuration = project.getConfiguration();
console.log(`Loaded project "${project.getName()}".`);
const targetNumber = parseInt(targetId);
const target = configuration.targets.find((target, index) => target.id === targetId || index + 1 === targetNumber);
if (!target) {
const targetsText = `${configuration.targets
.map((target, index) => `${index + 1}. ${target.id} (${target.name})`)
.join('\n')}`;
console.error(`Target "${targetId}" does not exist.\n\nAvailable targets:\n${targetsText}`);
process.exit(1);
}
console.log(`Loaded target "${target.name}".`);
console.log();
const vendor = index_js_1.VENDORS[target.vendor];
const family = vendor.families[target.family];
const device = family.devices[target.device];
const packageName = vendor.packages[target.package];
console.log(`Vendor: ${vendor.name}`);
console.log(`Family: ${family.name}`);
console.log(`Device: ${device.name}`);
console.log(`Package: ${packageName}`);
console.log();
// Create output directory if necessary
if (shouldExecute) {
const targetDirectory = path_1.default.join(cwd, target.directory ?? '.');
if (!(await (0, util_js_2.exists)(targetDirectory))) {
await (0, promises_1.mkdir)(targetDirectory, {
recursive: true
});
}
}
if (command === 'yosys') {
const workerOptions = (0, index_js_1.getYosysSynthesisWorkerOptions)(project, target.id);
for (const step of workerOptions.steps) {
console.log([step.tool, ''].concat(step.commands).join('\n'));
console.log();
if (shouldExecute) {
const designFilePath = path_1.default.join(cwd, 'design.ys');
await (0, promises_1.writeFile)(designFilePath, step.commands.concat(['']).join('\n'), { encoding: 'utf-8' });
await (0, tool_js_1.executeTool)(step.tool, ['design.ys'], cwd);
await (0, promises_1.unlink)(designFilePath);
}
}
}
else if (command === 'nextpnr') {
const workerOptions = (0, index_js_1.getNextpnrWorkerOptions)(project, target.id);
for (const step of workerOptions.steps) {
console.log([step.tool].concat((0, util_js_1.formatArguments)(step.arguments)).join('\n'));
console.log();
if (shouldExecute) {
await (0, tool_js_1.executeTool)(step.tool, step.arguments, cwd);
}
}
}
else {
console.error(`Unknown command "${command}".`);
process.exit(1);
}
})();
//# sourceMappingURL=index.js.map