UNPKG

edacation

Version:

Library and CLI for interacting with Yosys and nextpnr.

158 lines 6.6 kB
#!/usr/bin/env node "use strict"; 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.getYosysWorkerOptions)(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