fix: boundary scan could not save
This commit is contained in:
parent
ba6ec73b84
commit
a6ac728cf1
|
@ -10,6 +10,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@svgdotjs/svg.js": "^3.2.4",
|
"@svgdotjs/svg.js": "^3.2.4",
|
||||||
"@types/lodash": "^4.17.16",
|
"@types/lodash": "^4.17.16",
|
||||||
|
"async-mutex": "^0.5.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"log-symbols": "^7.0.0",
|
"log-symbols": "^7.0.0",
|
||||||
"mathjs": "^14.4.0",
|
"mathjs": "^14.4.0",
|
||||||
|
@ -2006,6 +2007,15 @@
|
||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/async-mutex": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/autoprefixer": {
|
"node_modules/autoprefixer": {
|
||||||
"version": "10.4.21",
|
"version": "10.4.21",
|
||||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
||||||
|
@ -3700,6 +3710,12 @@
|
||||||
"integrity": "sha512-HjX/7HxQe2bXkbp8pHTjy4Ir9eHIDnDDsLDphhGqy6I9iZ/vD4QXWEIlrVRZsEX+kS2jIiiF/mnl0nKnPTiYFw==",
|
"integrity": "sha512-HjX/7HxQe2bXkbp8pHTjy4Ir9eHIDnDDsLDphhGqy6I9iZ/vD4QXWEIlrVRZsEX+kS2jIiiF/mnl0nKnPTiYFw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/tslib": {
|
||||||
|
"version": "2.8.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||||
|
"license": "0BSD"
|
||||||
|
},
|
||||||
"node_modules/typed-function": {
|
"node_modules/typed-function": {
|
||||||
"version": "4.2.1",
|
"version": "4.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.1.tgz",
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@svgdotjs/svg.js": "^3.2.4",
|
"@svgdotjs/svg.js": "^3.2.4",
|
||||||
"@types/lodash": "^4.17.16",
|
"@types/lodash": "^4.17.16",
|
||||||
|
"async-mutex": "^0.5.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"log-symbols": "^7.0.0",
|
"log-symbols": "^7.0.0",
|
||||||
"mathjs": "^14.4.0",
|
"mathjs": "^14.4.0",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -99,7 +99,6 @@ async function handleClick(event: Event): Promise<void> {
|
||||||
isUploading.value = true;
|
isUploading.value = true;
|
||||||
try {
|
try {
|
||||||
const ret = await props.uploadEvent(bitstream.value);
|
const ret = await props.uploadEvent(bitstream.value);
|
||||||
console.debug(`After upload bistream: ${bitstream.value}, result: ${ret}`);
|
|
||||||
if (isUndefined(props.downloadEvent)) {
|
if (isUndefined(props.downloadEvent)) {
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dialog.info("上传成功");
|
dialog.info("上传成功");
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<div>
|
<div>
|
||||||
<h1 class="font-bold text-center text-2xl">Jtag</h1>
|
<h1 class="font-bold text-center text-2xl">Jtag</h1>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<p class="grow">Jtag Addr: {{ props.jtagAddr }}</p>
|
<p class="grow">Jtag Addr: {{ eqps.boardAddr }}</p>
|
||||||
<p class="grow">Jtag Port: {{ props.jtagPort?.toString() }}</p>
|
<p class="grow">Jtag Port: {{ eqps.boardPort.toString() }}</p>
|
||||||
<div class="flex justify-between grow">
|
<div class="flex justify-between grow">
|
||||||
<p>IDCode: 0x{{ jtagIDCode.toString(16).padStart(8, "0") }}</p>
|
<p>IDCode: 0x{{ jtagIDCode.toString(16).padStart(8, "0").toUpperCase() }}</p>
|
||||||
<button class="btn btn-circle w-6 h-6" :onclick="getIDCode">
|
<button class="btn btn-circle w-6 h-6" :onclick="getIDCode">
|
||||||
<svg class="icon opacity-70 fill-primary" viewBox="0 0 1024 1024" version="1.1"
|
<svg class="icon opacity-70 fill-primary" viewBox="0 0 1024 1024" version="1.1"
|
||||||
xmlns="http://www.w3.org/2000/svg" p-id="4865" width="200" height="200">
|
xmlns="http://www.w3.org/2000/svg" p-id="4865" width="200" height="200">
|
||||||
|
@ -17,27 +17,30 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<UploadCard class="bg-base-200" :upload-event="uploadBitstream" :download-event="downloadBitstream"
|
<UploadCard class="bg-base-200" :upload-event="eqps.jtagUploadBitstream"
|
||||||
:bitstream-file="eqps.jtagBitstream" @update:bitstream-file="handleBitstreamChange">
|
:download-event="eqps.jtagDownloadBitstream" :bitstream-file="eqps.jtagBitstream"
|
||||||
|
@update:bitstream-file="handleBitstreamChange">
|
||||||
</UploadCard>
|
</UploadCard>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<button class="btn w-full btn-primary" :class="isEnableJtagBoundaryScan ? '' : 'btn-soft'"
|
<fieldset class="fieldset w-full">
|
||||||
|
<legend class="fieldset-legend text-sm">边界扫描刷新率 / Hz</legend>
|
||||||
|
<input type="number" class="input validator w-full" required placeholder="Type a number between 1 to 1000" min="1"
|
||||||
|
max="1000" v-model="jtagBoundaryScanFreq" title="Type a number between 1 to 1000" />
|
||||||
|
<p class="validator-hint">输入一个1 ~ 1000的数</p>
|
||||||
|
</fieldset>
|
||||||
|
<button class="btn w-full btn-primary" :class="eqps.enableJtagBoundaryScan ? '' : 'btn-soft'"
|
||||||
:onclick="toggleJtagBoundaryScan">
|
:onclick="toggleJtagBoundaryScan">
|
||||||
{{ isEnableJtagBoundaryScan ? "关闭边界扫描" : "启动边界扫描" }}
|
{{ eqps.enableJtagBoundaryScan ? "关闭边界扫描" : "启动边界扫描" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { JtagClient } from "@/APIClient";
|
|
||||||
import z from "zod";
|
import z from "zod";
|
||||||
import UploadCard from "@/components/UploadCard.vue";
|
import UploadCard from "@/components/UploadCard.vue";
|
||||||
import { useDialogStore } from "@/stores/dialog";
|
import { useDialogStore } from "@/stores/dialog";
|
||||||
import { Common } from "@/Common";
|
|
||||||
import { useEquipments } from "@/stores/equipments";
|
import { useEquipments } from "@/stores/equipments";
|
||||||
import { useConstraintsStore } from "@/stores/constraints";
|
import { computed, ref, watchEffect } from "vue";
|
||||||
import { isUndefined } from "lodash";
|
|
||||||
import { ref, watchEffect } from "vue";
|
|
||||||
|
|
||||||
interface CapsProps {
|
interface CapsProps {
|
||||||
jtagAddr?: string;
|
jtagAddr?: string;
|
||||||
|
@ -48,107 +51,38 @@ const props = withDefaults(defineProps<CapsProps>(), {});
|
||||||
|
|
||||||
// Global Stores
|
// Global Stores
|
||||||
const dialog = useDialogStore();
|
const dialog = useDialogStore();
|
||||||
const constrainsts = useConstraintsStore();
|
|
||||||
const eqps = useEquipments();
|
const eqps = useEquipments();
|
||||||
|
|
||||||
const jtagController = new JtagClient();
|
const jtagBoundaryScanFreq = computed({
|
||||||
const isEnableJtagBoundaryScan = ref(false);
|
get: () => eqps.jtagBoundaryScanFreq,
|
||||||
|
set: (val) => {
|
||||||
|
if (z.number().positive().max(1000).safeParse(val).success)
|
||||||
|
eqps.jtagBoundaryScanFreq = val;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// 使用传入的属性或默认值
|
// 使用传入的属性或默认值
|
||||||
const jtagIDCode = ref(0);
|
const jtagIDCode = ref(0xffff_ffff);
|
||||||
|
|
||||||
function handleBitstreamChange(file: File | undefined) {
|
function handleBitstreamChange(file: File | undefined) {
|
||||||
eqps.jtagBitstream = file;
|
eqps.jtagBitstream = file;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleJtagBoundaryScan() {
|
async function toggleJtagBoundaryScan() {
|
||||||
isEnableJtagBoundaryScan.value = !isEnableJtagBoundaryScan.value;
|
if (eqps.jtagClientMutex.isLocked()) {
|
||||||
if (isEnableJtagBoundaryScan.value) jtagBoundaryScan();
|
dialog.warn("Jtag正在被占用");
|
||||||
}
|
|
||||||
|
|
||||||
async function jtagBoundaryScan() {
|
|
||||||
try {
|
|
||||||
const portStates = await jtagController.boundaryScanLogicalPorts(
|
|
||||||
props.jtagAddr,
|
|
||||||
props.jtagPort,
|
|
||||||
);
|
|
||||||
|
|
||||||
constrainsts.batchSetConstraintStates(portStates);
|
|
||||||
} catch (error) {
|
|
||||||
dialog.error("边界扫描发生错误");
|
|
||||||
console.error(error);
|
|
||||||
isEnableJtagBoundaryScan.value = false;
|
|
||||||
} finally {
|
|
||||||
if (isEnableJtagBoundaryScan.value) setTimeout(jtagBoundaryScan, 50);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function uploadBitstream(bitstream: File): Promise<boolean> {
|
|
||||||
if (isUndefined(props.jtagAddr) || isUndefined(props.jtagPort)) {
|
|
||||||
dialog.error("开发板地址或端口空缺");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.debug(`Before upload bistream: ${bitstream}`);
|
|
||||||
const resp = await jtagController.uploadBitstream(
|
|
||||||
props.jtagAddr,
|
|
||||||
Common.toFileParameterOrNull(bitstream),
|
|
||||||
);
|
|
||||||
return resp;
|
|
||||||
} catch (e) {
|
|
||||||
dialog.error("上传错误");
|
|
||||||
console.error(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function downloadBitstream(): Promise<boolean> {
|
|
||||||
if (isUndefined(props.jtagAddr) || isUndefined(props.jtagPort)) {
|
|
||||||
dialog.error("开发板地址或端口空缺");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const resp = await jtagController.downloadBitstream(
|
|
||||||
props.jtagAddr,
|
|
||||||
props.jtagPort,
|
|
||||||
);
|
|
||||||
return resp;
|
|
||||||
} catch (e) {
|
|
||||||
dialog.error("上传错误");
|
|
||||||
console.error(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getIDCode(isQuiet: boolean = false) {
|
|
||||||
if (isUndefined(props.jtagAddr) || isUndefined(props.jtagPort)) {
|
|
||||||
dialog.error("开发板地址或端口空缺");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
eqps.enableJtagBoundaryScan = !eqps.enableJtagBoundaryScan;
|
||||||
const resp = await jtagController.getDeviceIDCode(
|
|
||||||
props.jtagAddr,
|
|
||||||
props.jtagPort,
|
|
||||||
);
|
|
||||||
jtagIDCode.value = resp;
|
|
||||||
} catch (e) {
|
|
||||||
if (!isQuiet) dialog.error("获取IDCode错误");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watchEffect(() => {
|
async function getIDCode(isQuiet: boolean = false) {
|
||||||
eqps.setAddr(props.jtagAddr);
|
jtagIDCode.value = await eqps.jtagGetIDCode(isQuiet);
|
||||||
eqps.setPort(props.jtagPort);
|
}
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(async () => {
|
||||||
if (
|
if (eqps.setAddr(props.jtagAddr) && eqps.setPort(props.jtagPort))
|
||||||
z.string().ip().safeParse(props.jtagAddr).success &&
|
|
||||||
z.number().positive().safeParse(props.jtagPort).success
|
|
||||||
)
|
|
||||||
getIDCode(true);
|
getIDCode(true);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,33 +1,126 @@
|
||||||
import { ref, computed } from 'vue'
|
import { ref, watchEffect } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { isString, isUndefined, toNumber } from 'lodash';
|
import { isString, toNumber, isUndefined } from 'lodash';
|
||||||
|
import { Common } from '@/Common';
|
||||||
import z from "zod"
|
import z from "zod"
|
||||||
import { isNumber } from 'mathjs';
|
import { isNumber } from 'mathjs';
|
||||||
|
import { JtagClient } from "@/APIClient";
|
||||||
|
import { Mutex, withTimeout } from 'async-mutex';
|
||||||
|
import { useConstraintsStore } from "@/stores/constraints";
|
||||||
|
import { useDialogStore } from './dialog';
|
||||||
|
|
||||||
export const useEquipments = defineStore('equipments', () => {
|
export const useEquipments = defineStore('equipments', () => {
|
||||||
const boardAddr = ref("127.0.0.1")
|
// Global Stores
|
||||||
const boardPort = ref(1234)
|
const constrainsts = useConstraintsStore();
|
||||||
const jtagBitstream = ref<File>()
|
const dialog = useDialogStore();
|
||||||
|
|
||||||
function setAddr(address: string | undefined) {
|
const boardAddr = ref("127.0.0.1");
|
||||||
if (isUndefined(address)) return;
|
const boardPort = ref(1234);
|
||||||
if (z.string().ip("4").safeParse(address).success)
|
const jtagBitstream = ref<File>();
|
||||||
|
const jtagBoundaryScanFreq = ref(10);
|
||||||
|
const jtagClientMutex = withTimeout(new Mutex(), 2000, new Error("JtagClient Mutex Timeout!"))
|
||||||
|
const jtagClient = new JtagClient();
|
||||||
|
|
||||||
|
const enableJtagBoundaryScan = ref(false);
|
||||||
|
|
||||||
|
function setAddr(address: string | undefined): boolean {
|
||||||
|
if (isString(address) && z.string().ip("4").safeParse(address).success) {
|
||||||
boardAddr.value = address;
|
boardAddr.value = address;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPort(port: string | number | undefined) {
|
return false;
|
||||||
if (isUndefined(port)) return;
|
}
|
||||||
|
|
||||||
|
function setPort(port: string | number | undefined): boolean {
|
||||||
if (isString(port) && port.length != 0) {
|
if (isString(port) && port.length != 0) {
|
||||||
const portNumber = toNumber(port);
|
const portNumber = toNumber(port);
|
||||||
if (z.number().nonnegative().max(65535).safeParse(portNumber).success)
|
if (z.number().nonnegative().max(65535).safeParse(portNumber).success) {
|
||||||
boardPort.value = portNumber;
|
boardPort.value = portNumber;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (isNumber(port)) {
|
else if (isNumber(port)) {
|
||||||
if (z.number().nonnegative().max(65535).safeParse(port).success)
|
if (z.number().nonnegative().max(65535).safeParse(port).success) {
|
||||||
boardPort.value = port;
|
boardPort.value = port;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (enableJtagBoundaryScan.value) jtagBoundaryScan();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function jtagBoundaryScan() {
|
||||||
|
const release = await jtagClientMutex.acquire();
|
||||||
|
try {
|
||||||
|
const portStates = await jtagClient.boundaryScanLogicalPorts(
|
||||||
|
boardAddr.value,
|
||||||
|
boardPort.value,
|
||||||
|
);
|
||||||
|
|
||||||
|
constrainsts.batchSetConstraintStates(portStates);
|
||||||
|
} catch (error) {
|
||||||
|
dialog.error("边界扫描发生错误");
|
||||||
|
console.error(error);
|
||||||
|
enableJtagBoundaryScan.value = false;
|
||||||
|
} finally {
|
||||||
|
release();
|
||||||
|
|
||||||
|
if (enableJtagBoundaryScan.value)
|
||||||
|
setTimeout(jtagBoundaryScan, 1000 / jtagBoundaryScanFreq.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function jtagUploadBitstream(bitstream: File): Promise<boolean> {
|
||||||
|
try {
|
||||||
|
const resp = await jtagClient.uploadBitstream(
|
||||||
|
boardAddr.value,
|
||||||
|
Common.toFileParameterOrNull(bitstream),
|
||||||
|
);
|
||||||
|
return resp;
|
||||||
|
} catch (e) {
|
||||||
|
dialog.error("上传错误");
|
||||||
|
console.error(e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function jtagDownloadBitstream(): Promise<boolean> {
|
||||||
|
const release = await jtagClientMutex.acquire();
|
||||||
|
try {
|
||||||
|
const resp = await jtagClient.downloadBitstream(
|
||||||
|
boardAddr.value,
|
||||||
|
boardPort.value
|
||||||
|
);
|
||||||
|
return resp;
|
||||||
|
} catch (e) {
|
||||||
|
dialog.error("上传错误");
|
||||||
|
console.error(e);
|
||||||
|
return false;
|
||||||
|
} finally {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function jtagGetIDCode(isQuiet: boolean = false): Promise<number> {
|
||||||
|
const release = await jtagClientMutex.acquire();
|
||||||
|
try {
|
||||||
|
const resp = await jtagClient.getDeviceIDCode(
|
||||||
|
boardAddr.value,
|
||||||
|
boardPort.value
|
||||||
|
);
|
||||||
|
return resp;
|
||||||
|
} catch (e) {
|
||||||
|
if (!isQuiet) dialog.error("获取IDCode错误");
|
||||||
|
return 0xffff_ffff;
|
||||||
|
} finally {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
boardAddr,
|
boardAddr,
|
||||||
|
@ -35,6 +128,13 @@ export const useEquipments = defineStore('equipments', () => {
|
||||||
setAddr,
|
setAddr,
|
||||||
setPort,
|
setPort,
|
||||||
jtagBitstream,
|
jtagBitstream,
|
||||||
|
jtagBoundaryScanFreq,
|
||||||
|
jtagClientMutex,
|
||||||
|
jtagClient,
|
||||||
|
jtagUploadBitstream,
|
||||||
|
jtagDownloadBitstream,
|
||||||
|
jtagGetIDCode,
|
||||||
|
enableJtagBoundaryScan,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,6 @@ async function uploadBitstream(bitstream: File): Promise<boolean> {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
console.debug(`Before upload bistream: ${bitstream}`);
|
|
||||||
const resp = await jtagController.uploadBitstream(
|
const resp = await jtagController.uploadBitstream(
|
||||||
boardAddress.value,
|
boardAddress.value,
|
||||||
Common.toFileParameterOrNull(bitstream),
|
Common.toFileParameterOrNull(bitstream),
|
||||||
|
|
Loading…
Reference in New Issue