feat: frontend add set jtag frequency

This commit is contained in:
SikongJueluo 2025-05-19 21:18:22 +08:00
parent a6ac728cf1
commit d754a881d7
No known key found for this signature in database
6 changed files with 184 additions and 16 deletions

View File

@ -367,7 +367,6 @@ public class JtagController : ControllerBase
[HttpPost("BoundaryScanLogicalPorts")] [HttpPost("BoundaryScanLogicalPorts")]
[EnableCors("Users")] [EnableCors("Users")]
[ProducesResponseType(typeof(Dictionary<string, bool>), StatusCodes.Status200OK)] [ProducesResponseType(typeof(Dictionary<string, bool>), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(string), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)] [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public async ValueTask<IResult> BoundaryScanLogicalPorts(string address, int port) public async ValueTask<IResult> BoundaryScanLogicalPorts(string address, int port)
{ {
@ -382,6 +381,31 @@ public class JtagController : ControllerBase
return TypedResults.Ok(ret.Value); return TypedResults.Ok(ret.Value);
} }
/// <summary>
/// [TODO:description]
/// </summary>
/// <param name="address">[TODO:parameter]</param>
/// <param name="port">[TODO:parameter]</param>
/// <param name="speed">[TODO:parameter]</param>
/// <returns>[TODO:return]</returns>
[HttpPost("SetSpeed")]
[EnableCors("Users")]
[ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]
public async ValueTask<IResult> SetSpeed(string address, int port, UInt32 speed)
{
var jtagCtrl = new JtagClient.Jtag(address, port);
var ret = await jtagCtrl.SetSpeed(speed);
if (!ret.IsSuccessful)
{
if (ret.Error is ArgumentException)
return TypedResults.BadRequest(ret.Error);
else return TypedResults.InternalServerError(ret.Error);
}
return TypedResults.Ok(ret.Value);
}
} }
/// <summary> /// <summary>

View File

@ -28,6 +28,10 @@ public static class JtagAddr
/// Jtag Write Command /// Jtag Write Command
/// </summary> /// </summary>
public const UInt32 WRITE_CMD = 0x10_00_00_03; public const UInt32 WRITE_CMD = 0x10_00_00_03;
/// <summary>
/// Jtag Speed Control
/// </summary>
public const UInt32 SPEED_CTRL = 0x10_00_00_04;
} }
/// <summary> /// <summary>
@ -833,10 +837,26 @@ public class Jtag
if (cellList.IsNull) return new(new Exception("Get boundary logical ports failed")); if (cellList.IsNull) return new(new Exception("Get boundary logical ports failed"));
var portStatus = new Dictionary<string, bool>(); var portStatus = new Dictionary<string, bool>();
foreach (var cell in cellList.Value) { foreach (var cell in cellList.Value)
{
portStatus.Add(cell.PortID ?? "UnknownPortID", bitArray.Value[cell.CellNumber]); portStatus.Add(cell.PortID ?? "UnknownPortID", bitArray.Value[cell.CellNumber]);
} }
return portStatus; return portStatus;
} }
public async ValueTask<Result<bool>> SetSpeed(UInt32 speed)
{
// Clear Data
await MsgBus.UDPServer.ClearUDPData(this.address);
logger.Trace($"Clear up udp server {this.address} receive data");
var ret = await WriteFIFO(
JtagAddr.SPEED_CTRL, (speed << 16) | speed,
JtagState.CMD_EXEC_FINISH, JtagState.CMD_EXEC_FINISH);
if (!ret.IsSuccessful) return new (ret.Error);
return ret.Value;
}
} }

View File

@ -724,6 +724,75 @@ export class JtagClient {
} }
return Promise.resolve<{ [key: string]: boolean; }>(null as any); return Promise.resolve<{ [key: string]: boolean; }>(null as any);
} }
/**
* [TODO:description]
* @param address (optional) [TODO:parameter]
* @param port (optional) [TODO:parameter]
* @param speed (optional) [TODO:parameter]
* @return [TODO:return]
*/
setSpeed(address: string | undefined, port: number | undefined, speed: number | undefined): Promise<boolean> {
let url_ = this.baseUrl + "/api/Jtag/SetSpeed?";
if (address === null)
throw new Error("The parameter 'address' cannot be null.");
else if (address !== undefined)
url_ += "address=" + encodeURIComponent("" + address) + "&";
if (port === null)
throw new Error("The parameter 'port' cannot be null.");
else if (port !== undefined)
url_ += "port=" + encodeURIComponent("" + port) + "&";
if (speed === null)
throw new Error("The parameter 'speed' cannot be null.");
else if (speed !== undefined)
url_ += "speed=" + encodeURIComponent("" + speed) + "&";
url_ = url_.replace(/[?&]$/, "");
let options_: RequestInit = {
method: "POST",
headers: {
"Accept": "application/json"
}
};
return this.http.fetch(url_, options_).then((_response: Response) => {
return this.processSetSpeed(_response);
});
}
protected processSetSpeed(response: Response): Promise<boolean> {
const status = response.status;
let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
if (status === 200) {
return response.text().then((_responseText) => {
let result200: any = null;
let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result200 = resultData200 !== undefined ? resultData200 : <any>null;
return result200;
});
} else if (status === 400) {
return response.text().then((_responseText) => {
let result400: any = null;
let resultData400 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result400 = resultData400 !== undefined ? resultData400 : <any>null;
return throwException("A server side error occurred.", status, _responseText, _headers, result400);
});
} else if (status === 500) {
return response.text().then((_responseText) => {
let result500: any = null;
let resultData500 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
result500 = Exception.fromJS(resultData500);
return throwException("A server side error occurred.", status, _responseText, _headers, result500);
});
} else if (status !== 200 && status !== 204) {
return response.text().then((_responseText) => {
return throwException("An unexpected server error occurred.", status, _responseText, _headers);
});
}
return Promise.resolve<boolean>(null as any);
}
} }
export class RemoteUpdateClient { export class RemoteUpdateClient {

View File

@ -10,7 +10,8 @@
</svg> </svg>
</div> </div>
<Teleport to="#ComponentCapabilities" v-if="selectecComponentID === props.componentId"> <Teleport to="#ComponentCapabilities" v-if="selectecComponentID === props.componentId">
<MotherBoardCaps :jtagAddr="props.boardAddr" :jtagPort="toNumber(props.boardPort)" /> <MotherBoardCaps :jtagAddr="props.boardAddr" :jtagPort="toNumber(props.boardPort)" :jtagFreq="jtagFreq"
@change-jtag-freq="changeJtagFreq" />
</Teleport> </Teleport>
</template> </template>
@ -35,6 +36,11 @@ const emit = defineEmits<{
const props = withDefaults(defineProps<MotherBoardProps>(), getDefaultProps()); const props = withDefaults(defineProps<MotherBoardProps>(), getDefaultProps());
const selectecComponentID = inject(CanvasCurrentSelectedComponentID, ref(null)); const selectecComponentID = inject(CanvasCurrentSelectedComponentID, ref(null));
const jtagFreq = ref("25 MHz");
function changeJtagFreq(text: string) {
jtagFreq.value = text;
}
// //
const width = computed(() => 800 * props.size); const width = computed(() => 800 * props.size);
const height = computed(() => 600 * props.size); const height = computed(() => 600 * props.size);

View File

@ -5,7 +5,9 @@
<p class="grow">Jtag Addr: {{ eqps.boardAddr }}</p> <p class="grow">Jtag Addr: {{ eqps.boardAddr }}</p>
<p class="grow">Jtag Port: {{ eqps.boardPort.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").toUpperCase() }}</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">
@ -22,16 +24,26 @@
@update:bitstream-file="handleBitstreamChange"> @update:bitstream-file="handleBitstreamChange">
</UploadCard> </UploadCard>
<div class="divider"></div> <div class="divider"></div>
<fieldset class="fieldset w-full"> <div class="w-full">
<legend class="fieldset-legend text-sm">边界扫描刷新率 / Hz</legend> <legend class="fieldset-legend text-sm mb-0.3">Jtag运行频率</legend>
<input type="number" class="input validator w-full" required placeholder="Type a number between 1 to 1000" min="1" <select class="select w-full" @change="handleSelectJtagSpeed" :value="props.jtagFreq">
max="1000" v-model="jtagBoundaryScanFreq" title="Type a number between 1 to 1000" /> <option v-for="option in selectJtagSpeedOptions" :value="option.id">
<p class="validator-hint">输入一个1 ~ 1000的数</p> {{ option.text }}
</fieldset> </option>
<button class="btn w-full btn-primary" :class="eqps.enableJtagBoundaryScan ? '' : 'btn-soft'" </select>
:onclick="toggleJtagBoundaryScan"> </div>
{{ eqps.enableJtagBoundaryScan ? "关闭边界扫描" : "启动边界扫描" }} <div class="flex flex-row items-center">
</button> <fieldset class="fieldset w-70">
<legend class="fieldset-legend text-sm">边界扫描刷新率 / Hz</legend>
<input type="number" class="input validator" 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 btn-primary grow mx-4" :class="eqps.enableJtagBoundaryScan ? '' : 'btn-soft'"
:onclick="toggleJtagBoundaryScan">
{{ eqps.enableJtagBoundaryScan ? "关闭边界扫描" : "启动边界扫描" }}
</button>
</div>
</div> </div>
</template> </template>
@ -45,10 +57,24 @@ import { computed, ref, watchEffect } from "vue";
interface CapsProps { interface CapsProps {
jtagAddr?: string; jtagAddr?: string;
jtagPort?: number; jtagPort?: number;
jtagFreq?: string;
} }
const emits = defineEmits<{
changeJtagFreq: [text: string];
}>();
const props = withDefaults(defineProps<CapsProps>(), {}); const props = withDefaults(defineProps<CapsProps>(), {});
const selectJtagSpeedOptions = ref([
{ text: "25 MHz", id: 1 },
{ text: "12.5 MHz", id: 2 },
{ text: "6.25 MHz", id: 3 },
{ text: "3.125 MHz", id: 4 },
{ text: "1562.5 KHz", id: 5 },
{ text: "781.25 KHz", id: 6 },
{ text: "390.625 KHz", id: 7 },
]);
// Global Stores // Global Stores
const dialog = useDialogStore(); const dialog = useDialogStore();
const eqps = useEquipments(); const eqps = useEquipments();
@ -68,6 +94,12 @@ function handleBitstreamChange(file: File | undefined) {
eqps.jtagBitstream = file; eqps.jtagBitstream = file;
} }
function handleSelectJtagSpeed(event: Event) {
const target = event.target as HTMLSelectElement;
eqps.jtagSetSpeed(target.selectedIndex);
emits("changeJtagFreq", target.value);
}
async function toggleJtagBoundaryScan() { async function toggleJtagBoundaryScan() {
if (eqps.jtagClientMutex.isLocked()) { if (eqps.jtagClientMutex.isLocked()) {
dialog.warn("Jtag正在被占用"); dialog.warn("Jtag正在被占用");

View File

@ -17,8 +17,8 @@ export const useEquipments = defineStore('equipments', () => {
const boardAddr = ref("127.0.0.1"); const boardAddr = ref("127.0.0.1");
const boardPort = ref(1234); const boardPort = ref(1234);
const jtagBitstream = ref<File>(); const jtagBitstream = ref<File>();
const jtagBoundaryScanFreq = ref(10); const jtagBoundaryScanFreq = ref(100);
const jtagClientMutex = withTimeout(new Mutex(), 2000, new Error("JtagClient Mutex Timeout!")) const jtagClientMutex = withTimeout(new Mutex(), 1000, new Error("JtagClient Mutex Timeout!"))
const jtagClient = new JtagClient(); const jtagClient = new JtagClient();
const enableJtagBoundaryScan = ref(false); const enableJtagBoundaryScan = ref(false);
@ -121,6 +121,22 @@ export const useEquipments = defineStore('equipments', () => {
} }
} }
async function jtagSetSpeed(speed: number): Promise<boolean> {
const release = await jtagClientMutex.acquire();
try {
const resp = await jtagClient.setSpeed(
boardAddr.value,
boardPort.value,
speed
);
return resp;
} catch (e) {
dialog.error("设置Jtag速度失败");
return false;
} finally {
release();
}
}
return { return {
boardAddr, boardAddr,
@ -134,6 +150,7 @@ export const useEquipments = defineStore('equipments', () => {
jtagUploadBitstream, jtagUploadBitstream,
jtagDownloadBitstream, jtagDownloadBitstream,
jtagGetIDCode, jtagGetIDCode,
jtagSetSpeed,
enableJtagBoundaryScan, enableJtagBoundaryScan,
} }
}) })